github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/test/cr_multi_blocks_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 one conflict-free operation while a user is unstaged. 6 7 package test 8 9 import "testing" 10 11 // bob writes a multi-block file while unmerged, no conflicts 12 func TestCrUnmergedWriteMultiblockFile(t *testing.T) { 13 test(t, 14 blockSize(20), blockChangeSize(20*1024), users("alice", "bob"), 15 as(alice, 16 mkdir("a"), 17 ), 18 as(bob, 19 disableUpdates(), 20 ), 21 as(alice, 22 write("a/foo", "hello"), 23 ), 24 as(bob, noSync(), 25 write("a/b", ntimesString(5, "0123456789")), 26 write("a/b", ntimesString(10, "0123456789")), 27 write("a/b", ntimesString(15, "0123456789")), 28 reenableUpdates(), 29 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 30 read("a/b", ntimesString(15, "0123456789")), 31 read("a/foo", "hello"), 32 ), 33 as(alice, 34 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 35 read("a/b", ntimesString(15, "0123456789")), 36 read("a/foo", "hello"), 37 ), 38 ) 39 } 40 41 // bob writes a multi-block file with sequential writes while 42 // unmerged, no conflicts 43 func TestCrUnmergedWriteSequentialMultiblockFile(t *testing.T) { 44 test(t, 45 blockSize(20), blockChangeSize(20*1024), users("alice", "bob"), 46 as(alice, 47 mkdir("a"), 48 ), 49 as(bob, 50 disableUpdates(), 51 ), 52 as(alice, 53 write("a/foo", "hello"), 54 ), 55 as(bob, noSync(), 56 write("a/b", ntimesString(5, "0123456789")), 57 pwriteBS("a/b", []byte(ntimesString(5, "0123456789")), 50), 58 pwriteBS("a/b", []byte(ntimesString(5, "0123456789")), 100), 59 reenableUpdates(), 60 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 61 read("a/b", ntimesString(15, "0123456789")), 62 read("a/foo", "hello"), 63 ), 64 as(alice, 65 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 66 read("a/b", ntimesString(15, "0123456789")), 67 read("a/foo", "hello"), 68 ), 69 ) 70 } 71 72 // bob writes a multi-block file that conflicts with a file created by alice 73 func TestCrConflictUnmergedWriteMultiblockFile(t *testing.T) { 74 test(t, 75 blockSize(20), blockChangeSize(20*1024), users("alice", "bob"), 76 as(alice, 77 mkdir("a"), 78 ), 79 as(bob, 80 disableUpdates(), 81 ), 82 as(alice, 83 write("a/b", "hello"), 84 ), 85 as(bob, noSync(), 86 write("a/b", ntimesString(15, "0123456789")), 87 reenableUpdates(), 88 lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}), 89 read("a/b", "hello"), 90 read(crname("a/b", bob), ntimesString(15, "0123456789")), 91 ), 92 as(alice, 93 lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}), 94 read("a/b", "hello"), 95 read(crname("a/b", bob), ntimesString(15, "0123456789")), 96 ), 97 ) 98 } 99 100 // alice writes a multi-block file that conflicts with a directory 101 // created by alice 102 func TestCrConflictMergedWriteMultiblockFile(t *testing.T) { 103 test(t, 104 blockSize(20), users("alice", "bob"), 105 as(alice, 106 mkdir("a"), 107 ), 108 as(bob, 109 disableUpdates(), 110 ), 111 as(alice, 112 write("a/b", ntimesString(15, "0123456789")), 113 ), 114 as(bob, noSync(), 115 write("a/b/c", "hello"), 116 reenableUpdates(), 117 lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}), 118 read("a/b/c", "hello"), 119 read(crname("a/b", alice), ntimesString(15, "0123456789")), 120 ), 121 as(alice, 122 lsdir("a/", m{"b$": "DIR", crnameEsc("b", alice): "FILE"}), 123 read("a/b/c", "hello"), 124 read(crname("a/b", alice), ntimesString(15, "0123456789")), 125 ), 126 ) 127 } 128 129 // bob writes a multi-block file when there's a conflict on another 130 // file. Regression test for KBFS-3770. 131 func TestCrConflictWriteMultiblockFileDuringOtherConflict(t *testing.T) { 132 test(t, 133 blockSize(20), blockChangeSize(100*1024), users("alice", "bob"), 134 as(alice, 135 mkdir("a"), 136 write("a/b", ntimesString(15, "0123456789")), 137 ), 138 as(bob, 139 disableUpdates(), 140 ), 141 as(alice, 142 write("a/c", "foo"), 143 ), 144 as(bob, noSync(), 145 write("a/c", "foo"), 146 pwriteBS("a/b", []byte(ntimesString(15, "9876543210")), 150), 147 reenableUpdates(), 148 lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}), 149 read("a/b", ntimesString(15, "0123456789")+ntimesString(15, "9876543210")), 150 read("a/c", "foo"), 151 read(crname("a/c", bob), "foo"), 152 ), 153 as(alice, 154 lsdir("a/", m{"b$": "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}), 155 read("a/b", ntimesString(15, "0123456789")+ntimesString(15, "9876543210")), 156 read("a/c", "foo"), 157 read(crname("a/c", bob), "foo"), 158 ), 159 ) 160 } 161 162 // bob resurrects a file that was removed by alice 163 func TestCrConflictWriteToRemovedMultiblockFile(t *testing.T) { 164 test(t, 165 blockSize(20), blockChangeSize(100*1024), users("alice", "bob"), 166 as(alice, 167 mkdir("a"), 168 write("a/b", ntimesString(15, "0123456789")), 169 ), 170 as(bob, 171 disableUpdates(), 172 ), 173 as(alice, 174 rm("a/b"), 175 ), 176 as(bob, noSync(), 177 write("a/b", ntimesString(15, "9876543210")), 178 reenableUpdates(), 179 lsdir("a/", m{"b$": "FILE"}), 180 read("a/b", ntimesString(15, "9876543210")), 181 ), 182 as(alice, 183 lsdir("a/", m{"b$": "FILE"}), 184 read("a/b", ntimesString(15, "9876543210")), 185 ), 186 ) 187 } 188 189 // bob makes a file that was removed by alice executable 190 func TestCrConflictSetexToRemovedMultiblockFile(t *testing.T) { 191 test(t, 192 skip("dokan", "SetEx is a non-op on Dokan, thus no conflict."), 193 blockSize(20), users("alice", "bob"), 194 as(alice, 195 mkdir("a"), 196 write("a/b", ntimesString(15, "0123456789")), 197 ), 198 as(bob, 199 disableUpdates(), 200 ), 201 as(alice, 202 rm("a/b"), 203 ), 204 as(bob, noSync(), 205 setex("a/b", true), 206 reenableUpdates(), 207 lsdir("a/", m{"b$": "EXEC"}), 208 read("a/b", ntimesString(15, "0123456789")), 209 ), 210 as(alice, 211 lsdir("a/", m{"b$": "EXEC"}), 212 read("a/b", ntimesString(15, "0123456789")), 213 ), 214 ) 215 } 216 217 // bob moves a file that was removed by alice 218 func TestCrConflictMoveRemovedMultiblockFile(t *testing.T) { 219 test(t, 220 blockSize(20), users("alice", "bob"), 221 as(alice, 222 mkdir("a"), 223 write("a/b", ntimesString(15, "0123456789")), 224 ), 225 as(bob, 226 disableUpdates(), 227 ), 228 as(alice, 229 rm("a/b"), 230 ), 231 as(bob, noSync(), 232 rename("a/b", "a/c"), 233 reenableUpdates(), 234 lsdir("a/", m{"c$": "FILE"}), 235 read("a/c", ntimesString(15, "0123456789")), 236 ), 237 as(alice, 238 lsdir("a/", m{"c$": "FILE"}), 239 read("a/c", ntimesString(15, "0123456789")), 240 ), 241 ) 242 } 243 244 // bob writes a multi-block file while unmerged and the block change 245 // size is small, no conflicts. 246 func TestCrUnmergedWriteMultiblockFileWithSmallBlockChangeSize(t *testing.T) { 247 test(t, 248 blockSize(100), blockChangeSize(5), users("alice", "bob"), 249 as(alice, 250 mkdir("a"), 251 ), 252 as(bob, 253 disableUpdates(), 254 ), 255 as(alice, 256 write("a/foo", "hello"), 257 ), 258 as(bob, noSync(), 259 write("a/b", ntimesString(15, "0123456789")), 260 reenableUpdates(), 261 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 262 read("a/b", ntimesString(15, "0123456789")), 263 read("a/foo", "hello"), 264 ), 265 as(alice, 266 lsdir("a/", m{"b": "FILE", "foo": "FILE"}), 267 read("a/b", ntimesString(15, "0123456789")), 268 read("a/foo", "hello"), 269 ), 270 ) 271 } 272 273 // bob moves a multi-block file, and then deletes its parents. 274 func TestCrUnmergedMoveAndDeleteMultiblockFile(t *testing.T) { 275 test(t, 276 blockSize(20), users("alice", "bob"), 277 as(alice, 278 write("a/b/c/d", ntimesString(15, "0123456789")), 279 ), 280 as(bob, 281 disableUpdates(), 282 ), 283 as(alice, 284 write("foo", "bar"), 285 ), 286 as(bob, noSync(), 287 rename("a/b/c/d", "a/b/c/e"), 288 rm("a/b/c/e"), 289 rmdir("a/b/c"), 290 rmdir("a/b"), 291 reenableUpdates(), 292 lsdir("a/", m{}), 293 read("foo", "bar"), 294 ), 295 as(alice, 296 lsdir("a/", m{}), 297 read("foo", "bar"), 298 ), 299 ) 300 } 301 302 // alice writes a multi-block directory in separate batches, and bob reads it. 303 func TestCrWriteMultiblockDirMerge(t *testing.T) { 304 test(t, 305 blockSize(20), users("alice", "bob"), 306 as(alice, 307 mkfile("a/b", "b"), 308 mkfile("a/c", "c"), 309 ), 310 as(bob, 311 disableUpdates(), 312 ), 313 as(alice, 314 mkfile("a/d", "d"), 315 mkfile("a/e", "e"), 316 ), 317 as(bob, noSync(), 318 mkfile("a/f", "f"), 319 mkfile("a/g", "g"), 320 reenableUpdates(), 321 lsdir("a/", m{ 322 "b": "FILE", 323 "c": "FILE", 324 "d": "FILE", 325 "e": "FILE", 326 "f": "FILE", 327 "g": "FILE", 328 }), 329 read("a/b", "b"), 330 read("a/c", "c"), 331 read("a/d", "d"), 332 read("a/e", "e"), 333 read("a/f", "f"), 334 read("a/g", "g"), 335 ), 336 as(alice, 337 lsdir("a/", m{ 338 "b": "FILE", 339 "c": "FILE", 340 "d": "FILE", 341 "e": "FILE", 342 "f": "FILE", 343 "g": "FILE", 344 }), 345 read("a/b", "b"), 346 read("a/c", "c"), 347 read("a/d", "d"), 348 read("a/e", "e"), 349 read("a/f", "f"), 350 read("a/g", "g"), 351 ), 352 ) 353 } 354 355 // alice writes a multi-level, multi-block directory structure. 356 func TestCrRemoveMultilevelMultiblockDir(t *testing.T) { 357 test(t, 358 blockSize(20), users("alice", "bob"), 359 as(alice, 360 mkfile("a/b", "b"), 361 mkfile("a/c", "c"), 362 mkdir("a/d"), 363 mkfile("a/d/e", "e"), 364 mkfile("a/d/f", "f"), 365 mkdir("a/g"), 366 mkfile("a/g/h", "h"), 367 mkfile("a/g/i", "i"), 368 ), 369 as(bob, 370 disableUpdates(), 371 ), 372 as(alice, 373 mkdir("b"), 374 ), 375 as(bob, noSync(), 376 rm("a/g/i"), 377 rm("a/g/h"), 378 rmdir("a/g"), 379 rm("a/d/f"), 380 rm("a/d/e"), 381 rmdir("a/d"), 382 rm("a/c"), 383 rm("a/b"), 384 rmdir("a"), 385 reenableUpdates(), 386 lsdir("", m{"b": "DIR"}), 387 ), 388 as(alice, 389 lsdir("", m{"b": "DIR"}), 390 ), 391 ) 392 } 393 394 func TestCrDoubleResolutionMultiblock(t *testing.T) { 395 testCrDoubleResolution(t, 20) 396 }