github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/test/cr_basic_conflicts_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  // These tests all do multiple operations while a user is unstaged.
     6  
     7  package test
     8  
     9  import (
    10  	"os"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/keybase/client/go/kbfs/libkbfs"
    15  )
    16  
    17  // bob and alice both write(to the same file),
    18  func testCrConflictWriteFile(t *testing.T) {
    19  	test(t,
    20  		users("alice", "bob"),
    21  		as(alice,
    22  			mkfile("a/b", "hello"),
    23  		),
    24  		as(bob,
    25  			disableUpdates(),
    26  		),
    27  		as(alice,
    28  			write("a/b", "world"),
    29  		),
    30  		as(bob, noSync(),
    31  			write("a/b", "uh oh"),
    32  			reenableUpdates(),
    33  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
    34  			read("a/b", "world"),
    35  			read(crname("a/b", bob), "uh oh"),
    36  		),
    37  		as(alice,
    38  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
    39  			read("a/b", "world"),
    40  			read(crname("a/b", bob), "uh oh"),
    41  		),
    42  	)
    43  }
    44  
    45  func TestCrConflictWriteFile(t *testing.T) {
    46  	testCrConflictWriteFile(t)
    47  }
    48  
    49  func TestCrConflictWriteFileWithObfuscation(t *testing.T) {
    50  	oldEnv := os.Getenv(libkbfs.EnvKeybaseTestObfuscateLogsForTest)
    51  	os.Setenv(libkbfs.EnvKeybaseTestObfuscateLogsForTest, "1")
    52  	defer func() {
    53  		os.Setenv(libkbfs.EnvKeybaseTestObfuscateLogsForTest, oldEnv)
    54  	}()
    55  	testCrConflictWriteFile(t)
    56  }
    57  
    58  // bob and alice both create the same entry with different types
    59  func TestCrConflictCreateWithDifferentTypes(t *testing.T) {
    60  	test(t,
    61  		users("alice", "bob"),
    62  		as(alice,
    63  			mkdir("a"),
    64  			mkfile("a/b", "hello"),
    65  		),
    66  		as(bob,
    67  			disableUpdates(),
    68  		),
    69  		as(alice,
    70  			mkdir("a/c"),
    71  		),
    72  		as(bob, noSync(),
    73  			mkfile("a/c", ""),
    74  			reenableUpdates(),
    75  			lsdir("a/", m{"b$": "FILE", "c$": "DIR",
    76  				crnameEsc("c", bob): "FILE"}),
    77  			read("a/b", "hello"),
    78  			lsdir("a/c", m{}),
    79  			read(crname("a/c", bob), ""),
    80  		),
    81  		as(alice,
    82  			lsdir("a/", m{"b$": "FILE", "c$": "DIR",
    83  				crnameEsc("c", bob): "FILE"}),
    84  			read("a/b", "hello"),
    85  			lsdir("a/c", m{}),
    86  			read(crname("a/c", bob), ""),
    87  		),
    88  	)
    89  }
    90  
    91  // bob and alice both create the same file with different types
    92  func TestCrConflictCreateFileWithDifferentTypes(t *testing.T) {
    93  	test(t,
    94  		skip("dokan", "Does not work with Dokan."),
    95  		users("alice", "bob"),
    96  		as(alice,
    97  			mkdir("a"),
    98  			mkfile("a/b", "hello"),
    99  		),
   100  		as(bob,
   101  			disableUpdates(),
   102  		),
   103  		as(alice,
   104  			mkfile("a/c", ""),
   105  		),
   106  		as(bob, noSync(),
   107  			link("a/c", "b"),
   108  			reenableUpdates(),
   109  			lsdir("a/", m{"b$": "FILE", "c$": "FILE",
   110  				crnameEsc("c", bob): "SYM"}),
   111  			read("a/b", "hello"),
   112  			read("a/c", ""),
   113  			read(crname("a/c", bob), "hello"),
   114  		),
   115  		as(alice,
   116  			lsdir("a/", m{"b$": "FILE", "c$": "FILE",
   117  				crnameEsc("c", bob): "SYM"}),
   118  			read("a/b", "hello"),
   119  			read("a/c", ""),
   120  			read(crname("a/c", bob), "hello"),
   121  		),
   122  	)
   123  }
   124  
   125  // bob and alice both create the same symlink with different contents
   126  func TestCrConflictCreateSymlinkWithDifferentContents(t *testing.T) {
   127  	test(t,
   128  		skip("dokan", "Does not work with Dokan."),
   129  		users("alice", "bob"),
   130  		as(alice,
   131  			mkdir("a"),
   132  			mkfile("a/b", "hello"),
   133  			mkfile("a/c", "world"),
   134  		),
   135  		as(bob,
   136  			disableUpdates(),
   137  		),
   138  		as(alice,
   139  			link("a/d", "b"),
   140  		),
   141  		as(bob, noSync(),
   142  			link("a/d", "c"),
   143  			reenableUpdates(),
   144  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", "d$": "SYM",
   145  				crnameEsc("d", bob): "SYM"}),
   146  			read("a/b", "hello"),
   147  			read("a/c", "world"),
   148  			read("a/d", "hello"),
   149  			read(crname("a/d", bob), "world"),
   150  		),
   151  		as(alice,
   152  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", "d$": "SYM",
   153  				crnameEsc("d", bob): "SYM"}),
   154  			read("a/b", "hello"),
   155  			read("a/c", "world"),
   156  			read("a/d", "hello"),
   157  			read(crname("a/d", bob), "world"),
   158  		),
   159  	)
   160  }
   161  
   162  // bob and alice both write(to the same file), but on a non-default day.
   163  func TestCrConflictWriteFileWithAddTime(t *testing.T) {
   164  	timeInc := 25 * time.Hour
   165  	test(t,
   166  		users("alice", "bob"),
   167  		as(alice,
   168  			mkfile("a/b", "hello"),
   169  		),
   170  		as(bob,
   171  			disableUpdates(),
   172  		),
   173  		as(alice,
   174  			addTime(timeInc),
   175  			write("a/b", "world"),
   176  		),
   177  		as(bob, noSync(),
   178  			write("a/b", "uh oh"),
   179  			reenableUpdates(),
   180  			lsdir("a/", m{"b$": "FILE",
   181  				crnameAtTimeEsc("b", bob, timeInc): "FILE"}),
   182  			read("a/b", "world"),
   183  			read(crnameAtTime("a/b", bob, timeInc), "uh oh"),
   184  		),
   185  		as(alice,
   186  			lsdir("a/", m{"b$": "FILE",
   187  				crnameAtTimeEsc("b", bob, timeInc): "FILE"}),
   188  			read("a/b", "world"),
   189  			read(crnameAtTime("a/b", bob, timeInc), "uh oh"),
   190  		),
   191  	)
   192  }
   193  
   194  // bob and alice both write(to the same file),
   195  func TestCrConflictWriteFileWithExtension(t *testing.T) {
   196  	test(t,
   197  		users("alice", "bob"),
   198  		as(alice,
   199  			mkfile("a/foo.tar.gz", "hello"),
   200  		),
   201  		as(bob,
   202  			disableUpdates(),
   203  		),
   204  		as(alice,
   205  			write("a/foo.tar.gz", "world"),
   206  		),
   207  		as(bob, noSync(),
   208  			write("a/foo.tar.gz", "uh oh"),
   209  			reenableUpdates(),
   210  			lsdir("a/", m{"foo.tar.gz$": "FILE", crnameEsc("foo.tar.gz", bob): "FILE"}),
   211  			read("a/foo.tar.gz", "world"),
   212  			read(crname("a/foo.tar.gz", bob), "uh oh"),
   213  		),
   214  		as(alice,
   215  			lsdir("a/", m{"foo.tar.gz$": "FILE", crnameEsc("foo.tar.gz", bob): "FILE"}),
   216  			read("a/foo.tar.gz", "world"),
   217  			read(crname("a/foo.tar.gz", bob), "uh oh"),
   218  		),
   219  	)
   220  }
   221  
   222  // bob and alice both create the same file
   223  func TestCrConflictCreateFile(t *testing.T) {
   224  	test(t,
   225  		users("alice", "bob"),
   226  		as(alice,
   227  			mkdir("a"),
   228  		),
   229  		as(bob,
   230  			disableUpdates(),
   231  		),
   232  		as(alice,
   233  			write("a/b", "world"),
   234  		),
   235  		as(bob, noSync(),
   236  			write("a/b", "uh oh"),
   237  			reenableUpdates(),
   238  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   239  			read("a/b", "world"),
   240  			read(crname("a/b", bob), "uh oh"),
   241  		),
   242  		as(alice,
   243  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   244  			read("a/b", "world"),
   245  			read(crname("a/b", bob), "uh oh"),
   246  		),
   247  	)
   248  }
   249  
   250  // alice writes and removes a file, while bob writes it. Regression
   251  // test for KBFS-2507.
   252  func TestCrConflictWriteVsModifiedRemovedFile(t *testing.T) {
   253  	test(t,
   254  		skip("dokan", "SetEx is a no-op on Dokan, thus no conflict"),
   255  		users("alice", "bob"),
   256  		as(alice,
   257  			mkfile("a", "hello"),
   258  		),
   259  		as(bob,
   260  			disableUpdates(),
   261  		),
   262  		as(alice,
   263  			write("a", "goodbye"),
   264  			rm("a"),
   265  		),
   266  		as(bob, noSync(),
   267  			write("a", "hello world"),
   268  			reenableUpdates(),
   269  			lsdir("", m{"a$": "FILE"}),
   270  			read("a", "hello world"),
   271  		),
   272  		as(alice,
   273  			lsdir("", m{"a$": "FILE"}),
   274  			read("a", "hello world"),
   275  		),
   276  	)
   277  }
   278  
   279  // alice setattr's a file, while bob removes, recreates and writes to
   280  // a file of the same name. Regression test for KBFS-668.
   281  func TestCrConflictSetattrVsRecreatedFileInRoot(t *testing.T) {
   282  	test(t,
   283  		skip("dokan", "SetEx is a no-op on Dokan, thus no conflict"),
   284  		users("alice", "bob"),
   285  		as(alice,
   286  			mkfile("a", "hello"),
   287  		),
   288  		as(bob,
   289  			disableUpdates(),
   290  		),
   291  		as(alice,
   292  			setex("a", true),
   293  		),
   294  		as(bob, noSync(),
   295  			write("a", "uh oh"),
   296  			rm("a"),
   297  			mkfile("a", "world"),
   298  			reenableUpdates(),
   299  			lsdir("", m{"a$": "EXEC", crnameEsc("a", bob): "FILE"}),
   300  			read("a", "hello"),
   301  			read(crname("a", bob), "world"),
   302  			checkPrevRevisions("a", []uint8{1}),
   303  			checkPrevRevisions(crname("a", bob), []uint8{1}),
   304  		),
   305  		as(alice,
   306  			lsdir("", m{"a$": "EXEC", crnameEsc("a", bob): "FILE"}),
   307  			read("a", "hello"),
   308  			read(crname("a", bob), "world"),
   309  			checkPrevRevisions("a", []uint8{1}),
   310  			checkPrevRevisions(crname("a", bob), []uint8{1}),
   311  		),
   312  	)
   313  }
   314  
   315  // bob creates a directory with the same name that alice used for a file
   316  func TestCrConflictCauseRenameOfMergedFile(t *testing.T) {
   317  	test(t,
   318  		users("alice", "bob"),
   319  		as(alice,
   320  			mkdir("a"),
   321  		),
   322  		as(bob,
   323  			disableUpdates(),
   324  		),
   325  		as(alice,
   326  			write("a/b", "world"),
   327  		),
   328  		as(bob, noSync(),
   329  			write("a/b/c", "uh oh"),
   330  			reenableUpdates(),
   331  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}),
   332  			read(crname("a/b", alice), "world"),
   333  			read("a/b/c", "uh oh"),
   334  		),
   335  		as(alice,
   336  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}),
   337  			read(crname("a/b", alice), "world"),
   338  			read("a/b/c", "uh oh"),
   339  		),
   340  	)
   341  }
   342  
   343  // bob creates a directory with the same name that alice used for a
   344  // file that used to exist at that location
   345  func TestCrConflictCauseRenameOfMergedRecreatedFile(t *testing.T) {
   346  	test(t,
   347  		users("alice", "bob"),
   348  		as(alice,
   349  			mkdir("a"),
   350  			write("a/b", "hello"),
   351  		),
   352  		as(bob,
   353  			disableUpdates(),
   354  		),
   355  		as(alice,
   356  			write("a/b", "world"),
   357  		),
   358  		as(bob, noSync(),
   359  			rm("a/b"),
   360  			write("a/b/c", "uh oh"),
   361  			reenableUpdates(),
   362  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}),
   363  			read(crname("a/b", alice), "world"),
   364  			read("a/b/c", "uh oh"),
   365  			checkPrevRevisions("a", []uint8{1, 2, 3}),
   366  		),
   367  		as(alice,
   368  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}),
   369  			read(crname("a/b", alice), "world"),
   370  			read("a/b/c", "uh oh"),
   371  			checkPrevRevisions("a", []uint8{1, 2, 3}),
   372  		),
   373  	)
   374  }
   375  
   376  // bob renames a file over one modified by alice.
   377  func TestCrConflictUnmergedRenameFileOverModifiedFile(t *testing.T) {
   378  	test(t,
   379  		users("alice", "bob"),
   380  		as(alice,
   381  			write("a/b", "hello"),
   382  			write("a/c", "world"),
   383  		),
   384  		as(bob,
   385  			disableUpdates(),
   386  		),
   387  		as(alice,
   388  			write("a/b", "uh oh"),
   389  		),
   390  		as(bob, noSync(),
   391  			rename("a/c", "a/b"),
   392  			reenableUpdates(),
   393  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   394  			read("a/b", "uh oh"),
   395  			read(crname("a/b", bob), "world"),
   396  		),
   397  		as(alice,
   398  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   399  			read("a/b", "uh oh"),
   400  			read(crname("a/b", bob), "world"),
   401  		),
   402  	)
   403  }
   404  
   405  // bob modifies and renames a file that was modified by alice.
   406  func TestCrConflictUnmergedRenameModifiedFile(t *testing.T) {
   407  	test(t,
   408  		users("alice", "bob"),
   409  		as(alice,
   410  			write("a/b", "hello"),
   411  		),
   412  		as(bob,
   413  			disableUpdates(),
   414  		),
   415  		as(alice,
   416  			write("a/b", "world"),
   417  		),
   418  		as(bob, noSync(),
   419  			write("a/b", "uh oh"),
   420  			rename("a/b", "a/c"),
   421  			reenableUpdates(),
   422  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   423  			read("a/b", "world"),
   424  			read("a/c", "uh oh"),
   425  		),
   426  		as(alice,
   427  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   428  			read("a/b", "world"),
   429  			read("a/c", "uh oh"),
   430  		),
   431  	)
   432  }
   433  
   434  // bob modifies and renames a file that was modified by alice, while
   435  // alice also made a file with the new name.
   436  func TestCrConflictUnmergedRenameModifiedFileAndConflictFile(t *testing.T) {
   437  	test(t,
   438  		users("alice", "bob"),
   439  		as(alice,
   440  			write("a/b", "hello"),
   441  		),
   442  		as(bob,
   443  			disableUpdates(),
   444  		),
   445  		as(alice,
   446  			write("a/b", "world"),
   447  			mkfile("a/c", "CONFLICT"),
   448  		),
   449  		as(bob, noSync(),
   450  			write("a/b", "uh oh"),
   451  			rename("a/b", "a/c"),
   452  			reenableUpdates(),
   453  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
   454  			read("a/b", "world"),
   455  			read("a/c", "CONFLICT"),
   456  			read(crname("a/c", bob), "uh oh"),
   457  		),
   458  		as(alice,
   459  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
   460  			read("a/b", "world"),
   461  			read("a/c", "CONFLICT"),
   462  			read(crname("a/c", bob), "uh oh"),
   463  		),
   464  	)
   465  }
   466  
   467  // bob modifies and renames (to another dir) a file that was modified
   468  // by alice.
   469  func TestCrConflictUnmergedRenameAcrossDirsModifiedFile(t *testing.T) {
   470  	test(t,
   471  		users("alice", "bob"),
   472  		as(alice,
   473  			write("a/b", "hello"),
   474  		),
   475  		as(bob,
   476  			disableUpdates(),
   477  		),
   478  		as(alice,
   479  			write("a/b", "world"),
   480  		),
   481  		as(bob, noSync(),
   482  			write("a/b", "uh oh"),
   483  			rename("a/b", "b/c"),
   484  			reenableUpdates(),
   485  			lsdir("a/", m{"b$": "FILE"}),
   486  			read("a/b", "world"),
   487  			lsdir("b/", m{"c$": "FILE"}),
   488  			read("b/c", "uh oh"),
   489  		),
   490  		as(alice,
   491  			lsdir("a/", m{"b$": "FILE"}),
   492  			read("a/b", "world"),
   493  			lsdir("b/", m{"c$": "FILE"}),
   494  			read("b/c", "uh oh"),
   495  		),
   496  	)
   497  }
   498  
   499  // bob sets the mtime on and renames a file that had its mtime set by alice.
   500  func TestCrConflictUnmergedRenameSetMtimeFile(t *testing.T) {
   501  	targetMtime1 := time.Now().Add(1 * time.Minute)
   502  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
   503  	test(t,
   504  		users("alice", "bob"),
   505  		as(alice,
   506  			write("a/b", "hello"),
   507  		),
   508  		as(bob,
   509  			disableUpdates(),
   510  		),
   511  		as(alice,
   512  			setmtime("a/b", targetMtime1),
   513  		),
   514  		as(bob, noSync(),
   515  			setmtime("a/b", targetMtime2),
   516  			rename("a/b", "a/c"),
   517  			reenableUpdates(),
   518  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   519  			mtime("a/b", targetMtime1),
   520  			mtime("a/c", targetMtime2),
   521  		),
   522  		as(alice,
   523  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   524  			mtime("a/b", targetMtime1),
   525  			mtime("a/c", targetMtime2),
   526  		),
   527  	)
   528  }
   529  
   530  // bob renames a file from a new directory over one modified by alice.
   531  func TestCrConflictUnmergedRenameFileInNewDirOverModifiedFile(t *testing.T) {
   532  	test(t,
   533  		users("alice", "bob"),
   534  		as(alice,
   535  			write("a/b", "hello"),
   536  			write("a/c", "world"),
   537  		),
   538  		as(bob,
   539  			disableUpdates(),
   540  		),
   541  		as(alice,
   542  			write("a/b", "uh oh"),
   543  		),
   544  		as(bob, noSync(),
   545  			rename("a/c", "e/c"),
   546  			rename("e/c", "a/b"),
   547  			reenableUpdates(),
   548  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   549  			lsdir("e/", m{}),
   550  			read("a/b", "uh oh"),
   551  			read(crname("a/b", bob), "world"),
   552  		),
   553  		as(alice,
   554  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   555  			lsdir("e/", m{}),
   556  			read("a/b", "uh oh"),
   557  			read(crname("a/b", bob), "world"),
   558  		),
   559  	)
   560  }
   561  
   562  // bob renames an existing directory over one created by alice.
   563  // TODO: it would be better if this weren't a conflict.
   564  func TestCrConflictUnmergedRenamedDir(t *testing.T) {
   565  	test(t,
   566  		users("alice", "bob"),
   567  		as(alice,
   568  			write("a/b/c", "hello"),
   569  		),
   570  		as(bob,
   571  			disableUpdates(),
   572  		),
   573  		as(alice,
   574  			write("a/d/e", "world"),
   575  		),
   576  		as(bob, noSync(),
   577  			write("a/b/f", "uh oh"),
   578  			rename("a/b", "a/d"),
   579  			reenableUpdates(),
   580  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
   581  			lsdir("a/d", m{"e": "FILE"}),
   582  			lsdir(crname("a/d", bob), m{"c": "FILE", "f": "FILE"}),
   583  			read(crname("a/d", bob)+"/c", "hello"),
   584  			read("a/d/e", "world"),
   585  			read(crname("a/d", bob)+"/f", "uh oh"),
   586  		),
   587  		as(alice,
   588  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
   589  			lsdir("a/d", m{"e": "FILE"}),
   590  			lsdir(crname("a/d", bob), m{"c": "FILE", "f": "FILE"}),
   591  			read(crname("a/d", bob)+"/c", "hello"),
   592  			read("a/d/e", "world"),
   593  			read(crname("a/d", bob)+"/f", "uh oh"),
   594  		),
   595  	)
   596  }
   597  
   598  // bob renames a directory over one made non-empty by alice
   599  func TestCrConflictUnmergedRenameDirOverNonemptyDir(t *testing.T) {
   600  	test(t,
   601  		users("alice", "bob"),
   602  		as(alice,
   603  			mkdir("a/b"),
   604  			mkfile("a/c/d", "hello"),
   605  		),
   606  		as(bob,
   607  			disableUpdates(),
   608  		),
   609  		as(alice,
   610  			mkfile("a/b/e", "uh oh"),
   611  		),
   612  		as(bob, noSync(),
   613  			rm("a/b"),
   614  			rename("a/c", "a/b"),
   615  			reenableUpdates(),
   616  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "DIR"}),
   617  			lsdir("a/b", m{"e": "FILE"}),
   618  			lsdir(crname("a/b", bob), m{"d": "FILE"}),
   619  		),
   620  		as(alice,
   621  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "DIR"}),
   622  			lsdir("a/b", m{"e": "FILE"}),
   623  			lsdir(crname("a/b", bob), m{"d": "FILE"}),
   624  		),
   625  	)
   626  }
   627  
   628  // alice renames an existing directory over one created by bob. TODO:
   629  // it would be better if this weren't a conflict.
   630  func TestCrConflictMergedRenamedDir(t *testing.T) {
   631  	test(t,
   632  		users("alice", "bob"),
   633  		as(alice,
   634  			write("a/b/c", "hello"),
   635  		),
   636  		as(bob,
   637  			disableUpdates(),
   638  		),
   639  		as(alice,
   640  			write("a/b/f", "uh oh"),
   641  			rename("a/b", "a/d"),
   642  		),
   643  		as(bob, noSync(),
   644  			write("a/d/e", "world"),
   645  			reenableUpdates(),
   646  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
   647  			lsdir("a/d", m{"c": "FILE", "f": "FILE"}),
   648  			read("a/d/c", "hello"),
   649  			read(crname("a/d", bob)+"/e", "world"),
   650  			read("a/d/f", "uh oh"),
   651  		),
   652  		as(alice,
   653  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
   654  			lsdir("a/d", m{"c": "FILE", "f": "FILE"}),
   655  			read("a/d/c", "hello"),
   656  			read(crname("a/d", bob)+"/e", "world"),
   657  			read("a/d/f", "uh oh"),
   658  		),
   659  	)
   660  }
   661  
   662  // alice renames a file over one modified by bob.
   663  func TestCrConflictMergedRenameFileOverModifiedFile(t *testing.T) {
   664  	test(t,
   665  		users("alice", "bob"),
   666  		as(alice,
   667  			write("a/b", "hello"),
   668  			write("a/c", "world"),
   669  		),
   670  		as(bob,
   671  			disableUpdates(),
   672  		),
   673  		as(alice,
   674  			rename("a/c", "a/b"),
   675  		),
   676  		as(bob, noSync(),
   677  			write("a/b", "uh oh"),
   678  			reenableUpdates(),
   679  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   680  			read("a/b", "world"),
   681  			read(crname("a/b", bob), "uh oh"),
   682  		),
   683  		as(alice,
   684  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
   685  			read("a/b", "world"),
   686  			read(crname("a/b", bob), "uh oh"),
   687  		),
   688  	)
   689  }
   690  
   691  // alice modifies and renames a file that was modified by bob.
   692  func TestCrConflictMergedRenameModifiedFile(t *testing.T) {
   693  	test(t,
   694  		users("alice", "bob"),
   695  		as(alice,
   696  			write("a/b", "hello"),
   697  		),
   698  		as(bob,
   699  			disableUpdates(),
   700  		),
   701  		as(alice,
   702  			write("a/b", "world"),
   703  			rename("a/b", "a/c"),
   704  		),
   705  		as(bob, noSync(),
   706  			write("a/b", "uh oh"),
   707  			reenableUpdates(),
   708  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   709  			read("a/b", "uh oh"),
   710  			read("a/c", "world"),
   711  		),
   712  		as(alice,
   713  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   714  			read("a/b", "uh oh"),
   715  			read("a/c", "world"),
   716  		),
   717  	)
   718  }
   719  
   720  // alice modifies and renames a file that was modified by bob, while
   721  // bob also made a file with the new name.
   722  func TestCrConflictMergedRenameModifiedFileAndConflictFile(t *testing.T) {
   723  	test(t,
   724  		users("alice", "bob"),
   725  		as(alice,
   726  			write("a/b", "hello"),
   727  		),
   728  		as(bob,
   729  			disableUpdates(),
   730  		),
   731  		as(alice,
   732  			write("a/b", "uh oh"),
   733  			rename("a/b", "a/c"),
   734  		),
   735  		as(bob, noSync(),
   736  			write("a/b", "world"),
   737  			mkfile("a/c", "CONFLICT"),
   738  			reenableUpdates(),
   739  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
   740  			read("a/b", "world"),
   741  			read("a/c", "uh oh"),
   742  			read(crname("a/c", bob), "CONFLICT"),
   743  		),
   744  		as(alice,
   745  			lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
   746  			read("a/b", "world"),
   747  			read("a/c", "uh oh"),
   748  			read(crname("a/c", bob), "CONFLICT"),
   749  		),
   750  	)
   751  }
   752  
   753  // alice modifies and renames (to another dir) a file that was modified
   754  // by bob.
   755  func TestCrConflictMergedRenameAcrossDirsModifiedFile(t *testing.T) {
   756  	test(t,
   757  		users("alice", "bob"),
   758  		as(alice,
   759  			write("a/b", "hello"),
   760  		),
   761  		as(bob,
   762  			disableUpdates(),
   763  		),
   764  		as(alice,
   765  			write("a/b", "world"),
   766  			rename("a/b", "b/c"),
   767  		),
   768  		as(bob, noSync(),
   769  			write("a/b", "uh oh"),
   770  			reenableUpdates(),
   771  			lsdir("a/", m{"b$": "FILE"}),
   772  			read("a/b", "uh oh"),
   773  			lsdir("b/", m{"c$": "FILE"}),
   774  			read("b/c", "world"),
   775  		),
   776  		as(alice,
   777  			lsdir("a/", m{"b$": "FILE"}),
   778  			read("a/b", "uh oh"),
   779  			lsdir("b/", m{"c$": "FILE"}),
   780  			read("b/c", "world"),
   781  		),
   782  	)
   783  }
   784  
   785  // alice sets the mtime on and renames a file that had its mtime set by bob.
   786  func TestCrConflictMergedRenameSetMtimeFile(t *testing.T) {
   787  	targetMtime1 := time.Now().Add(1 * time.Minute)
   788  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
   789  	test(t,
   790  		users("alice", "bob"),
   791  		as(alice,
   792  			write("a/b", "hello"),
   793  		),
   794  		as(bob,
   795  			disableUpdates(),
   796  		),
   797  		as(alice,
   798  			setmtime("a/b", targetMtime1),
   799  			rename("a/b", "a/c"),
   800  		),
   801  		as(bob, noSync(),
   802  			setmtime("a/b", targetMtime2),
   803  			reenableUpdates(),
   804  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   805  			mtime("a/b", targetMtime2),
   806  			mtime("a/c", targetMtime1),
   807  		),
   808  		as(alice,
   809  			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
   810  			mtime("a/b", targetMtime2),
   811  			mtime("a/c", targetMtime1),
   812  		),
   813  	)
   814  }
   815  
   816  // alice sets the mtime on and renames a file that had its mtime set by bob.
   817  func TestCrConflictMergedRenameAcrossDirsSetMtimeFile(t *testing.T) {
   818  	targetMtime1 := time.Now().Add(1 * time.Minute)
   819  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
   820  	test(t,
   821  		users("alice", "bob"),
   822  		as(alice,
   823  			write("a/b", "hello"),
   824  		),
   825  		as(bob,
   826  			disableUpdates(),
   827  		),
   828  		as(alice,
   829  			setmtime("a/b", targetMtime1),
   830  			rename("a/b", "c"),
   831  		),
   832  		as(bob, noSync(),
   833  			setmtime("a/b", targetMtime2),
   834  			reenableUpdates(),
   835  			lsdir("", m{"a$": "DIR", "c$": "FILE"}),
   836  			lsdir("a/", m{"b$": "FILE"}),
   837  			mtime("a/b", targetMtime2),
   838  			mtime("c", targetMtime1),
   839  		),
   840  		as(alice,
   841  			lsdir("", m{"a$": "DIR", "c$": "FILE"}),
   842  			lsdir("a/", m{"b$": "FILE"}),
   843  			mtime("a/b", targetMtime2),
   844  			mtime("c", targetMtime1),
   845  		),
   846  	)
   847  }
   848  
   849  // alice and both both rename the same file, causing a copy.
   850  func TestCrConflictRenameSameFile(t *testing.T) {
   851  	test(t,
   852  		users("alice", "bob"),
   853  		as(alice,
   854  			write("a/b", "hello"),
   855  		),
   856  		as(bob,
   857  			disableUpdates(),
   858  		),
   859  		as(alice,
   860  			rename("a/b", "a/c"),
   861  		),
   862  		as(bob, noSync(),
   863  			rename("a/b", "a/d"),
   864  			reenableUpdates(),
   865  			lsdir("a/", m{"c": "FILE", "d": "FILE"}),
   866  			read("a/c", "hello"),
   867  			read("a/d", "hello"),
   868  		),
   869  		as(alice,
   870  			lsdir("a/", m{"c": "FILE", "d": "FILE"}),
   871  			read("a/c", "hello"),
   872  			read("a/d", "hello"),
   873  			write("a/c", "world"),
   874  		),
   875  		as(bob,
   876  			read("a/c", "world"),
   877  			read("a/d", "hello"),
   878  		),
   879  	)
   880  }
   881  
   882  // alice and both both rename the same executable file, causing a copy.
   883  func TestCrConflictRenameSameEx(t *testing.T) {
   884  	test(t,
   885  		users("alice", "bob"),
   886  		as(alice,
   887  			write("a/b", "hello"),
   888  			setex("a/b", true),
   889  		),
   890  		as(bob,
   891  			disableUpdates(),
   892  		),
   893  		as(alice,
   894  			rename("a/b", "a/c"),
   895  		),
   896  		as(bob, noSync(),
   897  			rename("a/b", "a/d"),
   898  			reenableUpdates(),
   899  			lsdir("a/", m{"c": "EXEC", "d": "EXEC"}),
   900  			read("a/c", "hello"),
   901  			read("a/d", "hello"),
   902  		),
   903  		as(alice,
   904  			lsdir("a/", m{"c": "EXEC", "d": "EXEC"}),
   905  			read("a/c", "hello"),
   906  			read("a/d", "hello"),
   907  			write("a/c", "world"),
   908  		),
   909  		as(bob,
   910  			read("a/c", "world"),
   911  			read("a/d", "hello"),
   912  		),
   913  	)
   914  }
   915  
   916  // alice and both both rename the same symlink.
   917  func TestCrConflictRenameSameSymlink(t *testing.T) {
   918  	test(t,
   919  		skip("dokan", "Does not work with Dokan."),
   920  		users("alice", "bob"),
   921  		as(alice,
   922  			write("a/foo", "hello"),
   923  			link("a/b", "foo"),
   924  		),
   925  		as(bob,
   926  			disableUpdates(),
   927  		),
   928  		as(alice,
   929  			rename("a/b", "a/c"),
   930  		),
   931  		as(bob, noSync(),
   932  			rename("a/b", "a/d"),
   933  			reenableUpdates(),
   934  			lsdir("a/", m{"foo": "FILE", "c": "SYM", "d": "SYM"}),
   935  			read("a/c", "hello"),
   936  			read("a/d", "hello"),
   937  		),
   938  		as(alice,
   939  			lsdir("a/", m{"foo": "FILE", "c": "SYM", "d": "SYM"}),
   940  			read("a/c", "hello"),
   941  			read("a/d", "hello"),
   942  			write("a/c", "world"),
   943  		),
   944  		as(bob,
   945  			read("a/c", "world"),
   946  			read("a/d", "world"),
   947  		),
   948  	)
   949  }
   950  
   951  // alice and bob both rename the same directory, causing a symlink to
   952  // be created.
   953  func TestCrConflictRenameSameDir(t *testing.T) {
   954  	test(t,
   955  		users("alice", "bob"),
   956  		as(alice,
   957  			write("a/b/c", "hello"),
   958  		),
   959  		as(bob,
   960  			disableUpdates(),
   961  		),
   962  		as(alice,
   963  			rename("a/b", "a/d"),
   964  		),
   965  		as(bob, noSync(),
   966  			rename("a/b", "a/e"),
   967  			reenableUpdates(),
   968  			lsdir("a/", m{"d": "DIR", "e": "SYM"}),
   969  			read("a/d/c", "hello"),
   970  			read("a/e/c", "hello"),
   971  		),
   972  		as(alice,
   973  			lsdir("a/", m{"d": "DIR", "e": "SYM"}),
   974  			read("a/d/c", "hello"),
   975  			read("a/e/c", "hello"),
   976  			write("a/d/f", "world"),
   977  			read("a/e/f", "world"),
   978  		),
   979  		as(bob,
   980  			read("a/e/f", "world"),
   981  		),
   982  	)
   983  }
   984  
   985  // alice and bob both rename the same directory, causing a symlink to
   986  // be created.
   987  func TestCrConflictRenameSameDirUpward(t *testing.T) {
   988  	test(t,
   989  		users("alice", "bob"),
   990  		as(alice,
   991  			write("a/b/c/d/e/foo", "hello"),
   992  		),
   993  		as(bob,
   994  			disableUpdates(),
   995  		),
   996  		as(alice,
   997  			rename("a/b/c/d/e", "a/e"),
   998  		),
   999  		as(bob, noSync(),
  1000  			rename("a/b/c/d/e", "a/b/c/d/f"),
  1001  			reenableUpdates(),
  1002  			lsdir("a/", m{"b": "DIR", "e": "DIR"}),
  1003  			lsdir("a/e", m{"foo": "FILE"}),
  1004  			lsdir("a/b/c/d", m{"f": "SYM"}),
  1005  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1006  			read("a/e/foo", "hello"),
  1007  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1008  		),
  1009  		as(alice,
  1010  			lsdir("a/", m{"b": "DIR", "e": "DIR"}),
  1011  			lsdir("a/e", m{"foo": "FILE"}),
  1012  			lsdir("a/b/c/d", m{"f": "SYM"}),
  1013  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1014  			read("a/e/foo", "hello"),
  1015  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1016  			write("a/e/foo2", "world"),
  1017  		),
  1018  		as(bob,
  1019  			read("a/b/c/d/f/foo2", "world"),
  1020  		),
  1021  	)
  1022  }
  1023  
  1024  // alice and bob both rename the same directory, causing a symlink to
  1025  // be created.
  1026  func TestCrConflictRenameSameDirMergedUpward(t *testing.T) {
  1027  	test(t,
  1028  		users("alice", "bob"),
  1029  		as(alice,
  1030  			write("a/b/c/d/e/foo", "hello"),
  1031  		),
  1032  		as(bob,
  1033  			disableUpdates(),
  1034  		),
  1035  		as(alice,
  1036  			rename("a/b/c/d/e", "a/b/c/d/f"),
  1037  		),
  1038  		as(bob, noSync(),
  1039  			rename("a/b/c/d/e", "a/e"),
  1040  			reenableUpdates(),
  1041  			lsdir("a/", m{"b": "DIR", "e": "SYM"}),
  1042  			lsdir("a/e", m{"foo": "FILE"}),
  1043  			lsdir("a/b/c/d", m{"f": "DIR"}),
  1044  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1045  			read("a/e/foo", "hello"),
  1046  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1047  		),
  1048  		as(alice,
  1049  			lsdir("a/", m{"b": "DIR", "e": "SYM"}),
  1050  			lsdir("a/e", m{"foo": "FILE"}),
  1051  			lsdir("a/b/c/d", m{"f": "DIR"}),
  1052  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1053  			read("a/e/foo", "hello"),
  1054  			lsdir("a/b/c/d/f", m{"foo": "FILE"}),
  1055  			write("a/e/foo2", "world"),
  1056  		),
  1057  		as(bob,
  1058  			read("a/b/c/d/f/foo2", "world"),
  1059  		),
  1060  	)
  1061  }
  1062  
  1063  func TestCrConflictRenameSameDirDownward(t *testing.T) {
  1064  	test(t,
  1065  		users("alice", "bob"),
  1066  		as(alice,
  1067  			write("a/b/foo", "hello"),
  1068  		),
  1069  		as(bob,
  1070  			disableUpdates(),
  1071  		),
  1072  		as(alice,
  1073  			rename("a/b", "a/c/d/e/f"),
  1074  		),
  1075  		as(bob, noSync(),
  1076  			rename("a/b", "a/g"),
  1077  			reenableUpdates(),
  1078  			lsdir("a/", m{"c": "DIR", "g": "SYM"}),
  1079  			lsdir("a/c/d/e/f", m{"foo": "FILE"}),
  1080  			lsdir("a/g", m{"foo": "FILE"}),
  1081  			read("a/c/d/e/f/foo", "hello"),
  1082  			read("a/g/foo", "hello"),
  1083  		),
  1084  		as(alice,
  1085  			lsdir("a/", m{"c": "DIR", "g": "SYM"}),
  1086  			lsdir("a/c/d/e/f", m{"foo": "FILE"}),
  1087  			lsdir("a/g", m{"foo": "FILE"}),
  1088  			read("a/c/d/e/f/foo", "hello"),
  1089  			read("a/g/foo", "hello"),
  1090  			write("a/c/d/e/f/foo2", "world"),
  1091  		),
  1092  		as(bob,
  1093  			read("a/g/foo2", "world"),
  1094  		),
  1095  	)
  1096  }
  1097  
  1098  func TestCrConflictRenameSameDirSideways(t *testing.T) {
  1099  	test(t,
  1100  		users("alice", "bob"),
  1101  		as(alice,
  1102  			write("a/b/c/d/foo", "hello"),
  1103  		),
  1104  		as(bob,
  1105  			disableUpdates(),
  1106  		),
  1107  		as(alice,
  1108  			rename("a/b/c/d", "a/e/f/g"),
  1109  		),
  1110  		as(bob, noSync(),
  1111  			rename("a/b/c/d", "a/b/c/h"),
  1112  			reenableUpdates(),
  1113  			lsdir("a/e/f", m{"g": "DIR"}),
  1114  			lsdir("a/b/c", m{"h": "SYM"}),
  1115  			lsdir("a/e/f/g", m{"foo": "FILE"}),
  1116  			lsdir("a/b/c/h", m{"foo": "FILE"}),
  1117  			read("a/e/f/g/foo", "hello"),
  1118  			read("a/b/c/h/foo", "hello"),
  1119  		),
  1120  		as(alice,
  1121  			lsdir("a/e/f", m{"g": "DIR"}),
  1122  			lsdir("a/b/c", m{"h": "SYM"}),
  1123  			lsdir("a/e/f/g", m{"foo": "FILE"}),
  1124  			lsdir("a/b/c/h", m{"foo": "FILE"}),
  1125  			read("a/e/f/g/foo", "hello"),
  1126  			read("a/b/c/h/foo", "hello"),
  1127  			write("a/e/f/g/foo2", "world"),
  1128  		),
  1129  		as(bob,
  1130  			read("a/b/c/h/foo2", "world"),
  1131  		),
  1132  	)
  1133  }
  1134  
  1135  // bob renames an existing directory over one created by alice, twice.
  1136  // TODO: it would be better if this weren't a conflict.
  1137  func TestCrConflictUnmergedRenamedDirDouble(t *testing.T) {
  1138  	test(t,
  1139  		users("alice", "bob"),
  1140  		as(alice,
  1141  			write("a/b/c", "hello"),
  1142  		),
  1143  		as(bob,
  1144  			disableUpdates(),
  1145  		),
  1146  		as(alice,
  1147  			write("a/d/e", "world"),
  1148  		),
  1149  		as(bob, noSync(),
  1150  			write("a/b/f", "uh oh"),
  1151  			rename("a/b", "a/d"),
  1152  			reenableUpdates(),
  1153  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
  1154  			lsdir("a/d", m{"e": "FILE"}),
  1155  			lsdir(crname("a/d", bob), m{"c": "FILE", "f": "FILE"}),
  1156  			read(crname("a/d", bob)+"/c", "hello"),
  1157  			read("a/d/e", "world"),
  1158  			read(crname("a/d", bob)+"/f", "uh oh"),
  1159  		),
  1160  		as(alice,
  1161  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob): "DIR"}),
  1162  			lsdir("a/d", m{"e": "FILE"}),
  1163  			lsdir(crname("a/d", bob), m{"c": "FILE", "f": "FILE"}),
  1164  			read(crname("a/d", bob)+"/c", "hello"),
  1165  			read("a/d/e", "world"),
  1166  			read(crname("a/d", bob)+"/f", "uh oh"),
  1167  			rm("a/d/e"),
  1168  			rm("a/d"),
  1169  			write("a/b/c", "hello"),
  1170  		),
  1171  		as(bob,
  1172  			disableUpdates(),
  1173  		),
  1174  		as(alice,
  1175  			write("a/d/e", "world"),
  1176  		),
  1177  		as(bob, noSync(),
  1178  			write("a/b/f", "uh oh"),
  1179  			rename("a/b", "a/d"),
  1180  			reenableUpdates(),
  1181  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob) + "$": "DIR", crnameEsc("d", bob) + ` \(1\)`: "DIR"}),
  1182  			lsdir("a/d", m{"e": "FILE"}),
  1183  			lsdir(crname("a/d", bob)+" (1)", m{"c": "FILE", "f": "FILE"}),
  1184  			read("a/d/e", "world"),
  1185  		),
  1186  		as(alice,
  1187  			lsdir("a/", m{"d$": "DIR", crnameEsc("d", bob) + "$": "DIR", crnameEsc("d", bob) + ` \(1\)`: "DIR"}),
  1188  			lsdir("a/d", m{"e": "FILE"}),
  1189  			lsdir(crname("a/d", bob)+" (1)", m{"c": "FILE", "f": "FILE"}),
  1190  			read("a/d/e", "world"),
  1191  		),
  1192  	)
  1193  }
  1194  
  1195  // bob and alice both write(to the same file),
  1196  func TestCrConflictWriteFileDouble(t *testing.T) {
  1197  	test(t,
  1198  		users("alice", "bob"),
  1199  		as(alice,
  1200  			mkfile("a/b", "hello"),
  1201  		),
  1202  		as(bob,
  1203  			disableUpdates(),
  1204  		),
  1205  		as(alice,
  1206  			write("a/b", "world"),
  1207  		),
  1208  		as(bob, noSync(),
  1209  			write("a/b", "uh oh"),
  1210  			reenableUpdates(),
  1211  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1212  			read("a/b", "world"),
  1213  			read(crname("a/b", bob), "uh oh"),
  1214  		),
  1215  		as(alice,
  1216  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1217  			read("a/b", "world"),
  1218  			read(crname("a/b", bob), "uh oh"),
  1219  		),
  1220  		as(bob,
  1221  			disableUpdates(),
  1222  		),
  1223  		as(alice,
  1224  			write("a/b", "another write"),
  1225  		),
  1226  		as(bob, noSync(),
  1227  			write("a/b", "uh oh again!"),
  1228  			reenableUpdates(),
  1229  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob) + "$": "FILE", crnameEsc("b", bob) + ` \(1\)`: "FILE"}),
  1230  			read("a/b", "another write"),
  1231  			read(crname("a/b", bob), "uh oh"),
  1232  			read(crname("a/b", bob)+" (1)", "uh oh again!"),
  1233  		),
  1234  		as(alice,
  1235  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob) + "$": "FILE", crnameEsc("b", bob) + ` \(1\)`: "FILE"}),
  1236  			read("a/b", "another write"),
  1237  			read(crname("a/b", bob), "uh oh"),
  1238  			read(crname("a/b", bob)+" (1)", "uh oh again!"),
  1239  		),
  1240  	)
  1241  }
  1242  
  1243  // bob and alice both write(to the same file),
  1244  func TestCrConflictWriteFileDoubleWithExtensions(t *testing.T) {
  1245  	test(t,
  1246  		users("alice", "bob"),
  1247  		as(alice,
  1248  			mkfile("a/file.tar.gz", "hello"),
  1249  		),
  1250  		as(bob,
  1251  			disableUpdates(),
  1252  		),
  1253  		as(alice,
  1254  			write("a/file.tar.gz", "world"),
  1255  		),
  1256  		as(bob, noSync(),
  1257  			write("a/file.tar.gz", "uh oh"),
  1258  			reenableUpdates(),
  1259  			lsdir("a/", m{"file.tar.gz$": "FILE", crnameEsc("file.tar.gz", bob): "FILE"}),
  1260  			read("a/file.tar.gz", "world"),
  1261  			read(crname("a/file.tar.gz", bob), "uh oh"),
  1262  		),
  1263  		as(alice,
  1264  			lsdir("a/", m{"file.tar.gz$": "FILE", crnameEsc("file.tar.gz", bob): "FILE"}),
  1265  			read("a/file.tar.gz", "world"),
  1266  			read(crname("a/file.tar.gz", bob), "uh oh"),
  1267  		),
  1268  		as(bob,
  1269  			disableUpdates(),
  1270  		),
  1271  		as(alice,
  1272  			write("a/file.tar.gz", "another write"),
  1273  		),
  1274  		as(bob, noSync(),
  1275  			write("a/file.tar.gz", "uh oh again!"),
  1276  			reenableUpdates(),
  1277  			lsdir("a/", m{"file.tar.gz$": "FILE", crnameEsc("file.tar.gz", bob) + "$": "FILE", crnameEsc("file", bob) + ` \(1\).tar.gz`: "FILE"}),
  1278  			read("a/file.tar.gz", "another write"),
  1279  			read(crname("a/file.tar.gz", bob), "uh oh"),
  1280  			read(crname("a/file", bob)+" (1).tar.gz", "uh oh again!"),
  1281  		),
  1282  		as(alice,
  1283  			lsdir("a/", m{"file.tar.gz$": "FILE", crnameEsc("file.tar.gz", bob) + "$": "FILE", crnameEsc("file", bob) + ` \(1\).tar.gz`: "FILE"}),
  1284  			read("a/file.tar.gz", "another write"),
  1285  			read(crname("a/file.tar.gz", bob), "uh oh"),
  1286  			read(crname("a/file", bob)+" (1).tar.gz", "uh oh again!"),
  1287  		),
  1288  	)
  1289  }
  1290  
  1291  // bob causes a rename cycle with a conflict while unstaged.
  1292  func TestCrRenameCycleWithConflict(t *testing.T) {
  1293  	test(t,
  1294  		users("alice", "bob"),
  1295  		as(alice,
  1296  			mkdir("a"),
  1297  			mkdir("a/b"),
  1298  			mkdir("a/c"),
  1299  		),
  1300  		as(bob,
  1301  			disableUpdates(),
  1302  		),
  1303  		as(alice,
  1304  			rename("a/c", "a/b/c"),
  1305  		),
  1306  		as(bob, noSync(),
  1307  			rename("a/b", "a/c/b"),
  1308  			write("a/b", "uh oh"),
  1309  			reenableUpdates(),
  1310  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1311  			read(crname("a/b", bob), "uh oh"),
  1312  			lsdir("a/b/", m{"c": "DIR"}),
  1313  			lsdir("a/b/c", m{"b": "SYM"}),
  1314  			lsdir("a/b/c/b", m{"c": "DIR"}),
  1315  		),
  1316  		as(alice,
  1317  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1318  			read(crname("a/b", bob), "uh oh"),
  1319  			lsdir("a/b/", m{"c": "DIR"}),
  1320  			lsdir("a/b/c", m{"b": "SYM"}),
  1321  			lsdir("a/b/c/b", m{"c": "DIR"}),
  1322  			write("a/b/d", "hello"),
  1323  		),
  1324  		as(bob,
  1325  			read("a/b/c/b/d", "hello"),
  1326  		),
  1327  	)
  1328  }
  1329  
  1330  // bob causes a rename cycle with two conflicts while unstaged.
  1331  func TestCrRenameCycleWithTwoConflicts(t *testing.T) {
  1332  	test(t,
  1333  		users("alice", "bob"),
  1334  		as(alice,
  1335  			mkdir("a"),
  1336  			mkdir("a/b"),
  1337  			mkdir("a/c"),
  1338  		),
  1339  		as(bob,
  1340  			disableUpdates(),
  1341  		),
  1342  		as(alice,
  1343  			rename("a/c", "a/b/c"),
  1344  			write("a/b/c/b", "uh oh"),
  1345  		),
  1346  		as(bob, noSync(),
  1347  			rename("a/b", "a/c/b"),
  1348  			write("a/b", "double uh oh"),
  1349  			reenableUpdates(),
  1350  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1351  			read(crname("a/b", bob), "double uh oh"),
  1352  			lsdir("a/b/", m{"c": "DIR"}),
  1353  			lsdir("a/b/c", m{"b$": "SYM", crnameEsc("b", alice): "FILE"}),
  1354  			lsdir("a/b/c/b", m{"c": "DIR"}),
  1355  		),
  1356  		as(alice,
  1357  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1358  			read(crname("a/b", bob), "double uh oh"),
  1359  			lsdir("a/b/", m{"c": "DIR"}),
  1360  			lsdir("a/b/c", m{"b$": "SYM", crnameEsc("b", alice): "FILE"}),
  1361  			lsdir("a/b/c/b", m{"c": "DIR"}),
  1362  			write("a/b/d", "hello"),
  1363  		),
  1364  		as(bob,
  1365  			read("a/b/c/b/d", "hello"),
  1366  		),
  1367  	)
  1368  }
  1369  
  1370  // bob causes a rename cycle with two conflicts while unstaged.
  1371  func TestCrRenameCycleWithConflictAndMergedDir(t *testing.T) {
  1372  	test(t,
  1373  		users("alice", "bob"),
  1374  		as(alice,
  1375  			mkdir("a"),
  1376  			mkdir("a/b"),
  1377  			mkdir("a/c"),
  1378  		),
  1379  		as(bob,
  1380  			disableUpdates(),
  1381  		),
  1382  		as(alice,
  1383  			rename("a/c", "a/b/c"),
  1384  			mkdir("a/b/c/b"),
  1385  		),
  1386  		as(bob, noSync(),
  1387  			rename("a/b", "a/c/b"),
  1388  			write("a/b", "uh oh"),
  1389  			reenableUpdates(),
  1390  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1391  			read(crname("a/b", bob), "uh oh"),
  1392  			lsdir("a/b/", m{"c": "DIR"}),
  1393  			lsdir("a/b/c", m{"b$": "DIR", crnameEsc("b", bob): "SYM"}),
  1394  			lsdir(crname("a/b/c/b", bob), m{"c": "DIR"}),
  1395  			lsdir("a/b/c/b", m{}),
  1396  		),
  1397  		as(alice,
  1398  			lsdir("a/", m{"b$": "DIR", crnameEsc("b", bob): "FILE"}),
  1399  			read(crname("a/b", bob), "uh oh"),
  1400  			lsdir("a/b/", m{"c": "DIR"}),
  1401  			lsdir("a/b/c", m{"b$": "DIR", crnameEsc("b", bob): "SYM"}),
  1402  			lsdir(crname("a/b/c/b", bob), m{"c": "DIR"}),
  1403  			lsdir("a/b/c/b", m{}),
  1404  			write("a/b/d", "hello"),
  1405  		),
  1406  		as(bob,
  1407  			read(crname("a/b/c/b", bob)+"/d", "hello"),
  1408  		),
  1409  	)
  1410  }
  1411  
  1412  // alice and bob both truncate the same file to different sizes
  1413  func TestCrBothTruncateFileDifferentSizes(t *testing.T) {
  1414  	test(t,
  1415  		users("alice", "bob"),
  1416  		as(alice,
  1417  			mkfile("a/b", "hello"),
  1418  		),
  1419  		as(bob,
  1420  			disableUpdates(),
  1421  		),
  1422  		as(alice,
  1423  			truncate("a/b", 4),
  1424  		),
  1425  		as(bob, noSync(),
  1426  			truncate("a/b", 3),
  1427  			reenableUpdates(),
  1428  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1429  			read("a/b", "hell"),
  1430  			read(crname("a/b", bob), "hel"),
  1431  		),
  1432  		as(alice,
  1433  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1434  			read("a/b", "hell"),
  1435  			read(crname("a/b", bob), "hel"),
  1436  		),
  1437  	)
  1438  }
  1439  
  1440  // alice and bob both truncate the same file to different sizes, after
  1441  // truncating to the same size
  1442  func TestCrBothTruncateFileDifferentSizesAfterSameSize(t *testing.T) {
  1443  	test(t,
  1444  		users("alice", "bob"),
  1445  		as(alice,
  1446  			mkfile("a/b", "hello"),
  1447  		),
  1448  		as(bob,
  1449  			disableUpdates(),
  1450  		),
  1451  		as(alice,
  1452  			truncate("a/b", 0),
  1453  		),
  1454  		as(bob, noSync(),
  1455  			truncate("a/b", 0),
  1456  			truncate("a/b", 3),
  1457  			reenableUpdates(),
  1458  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1459  			read("a/b", ""),
  1460  			read(crname("a/b", bob), string(make([]byte, 3))),
  1461  		),
  1462  		as(alice,
  1463  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1464  			read("a/b", ""),
  1465  			read(crname("a/b", bob), string(make([]byte, 3))),
  1466  		),
  1467  	)
  1468  }
  1469  
  1470  // alice and bob both set the mtime on a file
  1471  func TestCrBothSetMtimeFile(t *testing.T) {
  1472  	targetMtime1 := time.Now().Add(1 * time.Minute)
  1473  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
  1474  	test(t,
  1475  		users("alice", "bob"),
  1476  		as(alice,
  1477  			mkfile("a/b", "hello"),
  1478  		),
  1479  		as(bob,
  1480  			disableUpdates(),
  1481  		),
  1482  		as(alice,
  1483  			setmtime("a/b", targetMtime1),
  1484  		),
  1485  		as(bob, noSync(),
  1486  			setmtime("a/b", targetMtime2),
  1487  			reenableUpdates(),
  1488  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1489  			mtime("a/b", targetMtime1),
  1490  			mtime(crname("a/b", bob), targetMtime2),
  1491  		),
  1492  		as(alice,
  1493  			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
  1494  			mtime("a/b", targetMtime1),
  1495  			mtime(crname("a/b", bob), targetMtime2),
  1496  		),
  1497  	)
  1498  }
  1499  
  1500  // alice and bob both set the mtime on a dir
  1501  func TestCrBothSetMtimeDir(t *testing.T) {
  1502  	targetMtime1 := time.Now().Add(1 * time.Minute)
  1503  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
  1504  	test(t,
  1505  		skip("dokan", "Dokan can't read mtimes on symlinks."),
  1506  		users("alice", "bob"),
  1507  		as(alice,
  1508  			mkdir("a"),
  1509  		),
  1510  		as(bob,
  1511  			disableUpdates(),
  1512  		),
  1513  		as(alice,
  1514  			setmtime("a", targetMtime1),
  1515  		),
  1516  		as(bob, noSync(),
  1517  			setmtime("a", targetMtime2),
  1518  			reenableUpdates(),
  1519  			lsdir("", m{"a$": "DIR", crnameEsc("a", bob): "SYM"}),
  1520  			mtime("a", targetMtime1),
  1521  			mtime(crname("a", bob), targetMtime2),
  1522  		),
  1523  		as(alice,
  1524  			lsdir("", m{"a$": "DIR", crnameEsc("a", bob): "SYM"}),
  1525  			mtime("a", targetMtime1),
  1526  			mtime(crname("a", bob), targetMtime2),
  1527  		),
  1528  	)
  1529  }
  1530  
  1531  // alice and bob both set the mtime on a dir, while bob writes a file
  1532  // in the same parent directory as the mtime'd dir.  Regression test
  1533  // for KBFS-3813 (which was already fixed by KBFS-3770).
  1534  func TestCrBothSetMtimeDirWithWrittenFile(t *testing.T) {
  1535  	targetMtime1 := time.Now().Add(1 * time.Minute)
  1536  	targetMtime2 := targetMtime1.Add(1 * time.Minute)
  1537  	test(t,
  1538  		skip("dokan", "Dokan can't read mtimes on symlinks."),
  1539  		users("alice", "bob"),
  1540  		as(alice,
  1541  			mkdir("a"),
  1542  			mkfile("b", "foo"),
  1543  		),
  1544  		as(bob,
  1545  			disableUpdates(),
  1546  		),
  1547  		as(alice,
  1548  			setmtime("a", targetMtime1),
  1549  		),
  1550  		as(bob, noSync(),
  1551  			setmtime("a", targetMtime2),
  1552  			write("b", "foo2"),
  1553  			reenableUpdates(),
  1554  			lsdir("", m{"a$": "DIR", crnameEsc("a", bob): "SYM", "b$": "FILE"}),
  1555  			mtime("a", targetMtime1),
  1556  			mtime(crname("a", bob), targetMtime2),
  1557  			read("b", "foo2"),
  1558  		),
  1559  		as(alice,
  1560  			lsdir("", m{"a$": "DIR", crnameEsc("a", bob): "SYM", "b$": "FILE"}),
  1561  			mtime("a", targetMtime1),
  1562  			mtime(crname("a", bob), targetMtime2),
  1563  			read("b", "foo2"),
  1564  		),
  1565  	)
  1566  }
  1567  
  1568  // alice and bob both create the same dir structure and make and
  1569  // rename the same file in it.  Regression for KBFS-2883.
  1570  func TestCrBothCreateSameRenamedFileInSameNewDir(t *testing.T) {
  1571  	test(t,
  1572  		users("alice", "bob"),
  1573  		as(alice,
  1574  			mkfile("a/b", "hello"),
  1575  		),
  1576  		as(bob,
  1577  			disableUpdates(),
  1578  		),
  1579  		as(alice,
  1580  			mkfile("a/c/d/e", "foo"),
  1581  			rename("a/c/d/e", "a/c/d/f"),
  1582  		),
  1583  		as(bob, noSync(),
  1584  			mkfile("a/c/d/e", "foo"),
  1585  			rename("a/c/d/e", "a/c/d/f"),
  1586  			reenableUpdates(),
  1587  			lsdir("a/", m{"b$": "FILE", "c$": "DIR"}),
  1588  			lsdir("a/c", m{"d$": "DIR"}),
  1589  			lsdir("a/c/d", m{"f$": "FILE", crnameEsc("f", bob): "FILE"}),
  1590  			read("a/c/d/f", "foo"),
  1591  			read(crname("a/c/d/f", bob), "foo"),
  1592  		),
  1593  		as(alice,
  1594  			lsdir("a/", m{"b$": "FILE", "c$": "DIR"}),
  1595  			lsdir("a/c", m{"d$": "DIR"}),
  1596  			lsdir("a/c/d", m{"f$": "FILE", crnameEsc("f", bob): "FILE"}),
  1597  			read("a/c/d/f", "foo"),
  1598  			read(crname("a/c/d/f", bob), "foo"),
  1599  		),
  1600  	)
  1601  }