github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/test/cr_complex_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 "testing" 11 "time" 12 ) 13 14 // bob renames a non-conflicting file into a new directory while unstaged 15 func TestCrUnmergedRenameIntoNewDir(t *testing.T) { 16 test(t, 17 users("alice", "bob"), 18 as(alice, 19 mkfile("a/b", "hello"), 20 ), 21 as(bob, 22 disableUpdates(), 23 ), 24 as(alice, 25 write("a/c", "world"), 26 ), 27 as(bob, noSync(), 28 rename("a/b", "d/e"), 29 reenableUpdates(), 30 lsdir("a/", m{"c": "FILE"}), 31 lsdir("d/", m{"e": "FILE"}), 32 read("a/c", "world"), 33 read("d/e", "hello"), 34 ), 35 as(alice, 36 lsdir("a/", m{"c": "FILE"}), 37 lsdir("d/", m{"e": "FILE"}), 38 read("a/c", "world"), 39 read("d/e", "hello"), 40 ), 41 ) 42 } 43 44 // alice renames a non-conflicting file into a new directory while 45 // bob is unstaged. 46 func TestCrMergedRenameIntoNewDir(t *testing.T) { 47 test(t, 48 users("alice", "bob"), 49 as(alice, 50 mkfile("a/b", "hello"), 51 ), 52 as(bob, 53 disableUpdates(), 54 ), 55 as(alice, 56 rename("a/b", "d/e"), 57 ), 58 as(bob, noSync(), 59 write("a/c", "world"), 60 reenableUpdates(), 61 lsdir("a/", m{"c": "FILE"}), 62 lsdir("d/", m{"e": "FILE"}), 63 read("a/c", "world"), 64 read("d/e", "hello"), 65 ), 66 as(alice, 67 lsdir("a/", m{"c": "FILE"}), 68 lsdir("d/", m{"e": "FILE"}), 69 read("a/c", "world"), 70 read("d/e", "hello"), 71 ), 72 ) 73 } 74 75 // bob causes a simple rename(cycle while unstaged), 76 func TestCrRenameCycle(t *testing.T) { 77 test(t, 78 users("alice", "bob"), 79 as(alice, 80 mkdir("a"), 81 mkdir("b"), 82 ), 83 as(bob, 84 disableUpdates(), 85 ), 86 as(alice, 87 rename("b", "a/b"), 88 ), 89 as(bob, noSync(), 90 rename("a", "b/a"), 91 reenableUpdates(), 92 lsdir("a/", m{"b": "DIR"}), 93 lsdir("a/b/", m{"a": "SYM"}), 94 lsdir("a/b/a", m{"b": "DIR"}), 95 ), 96 as(alice, 97 lsdir("a/", m{"b": "DIR"}), 98 lsdir("a/b/", m{"a": "SYM"}), 99 lsdir("a/b/a", m{"b": "DIR"}), 100 write("a/c", "hello"), 101 ), 102 as(bob, 103 read("a/b/a/c", "hello"), 104 ), 105 ) 106 } 107 108 // bob causes a complicated rename(cycle while unstaged), 109 func TestCrComplexRenameCycle(t *testing.T) { 110 test(t, 111 users("alice", "bob"), 112 as(alice, 113 mkdir("a"), 114 mkdir("b"), 115 ), 116 as(bob, 117 disableUpdates(), 118 ), 119 as(alice, 120 rename("b", "a/b"), 121 ), 122 as(bob, noSync(), 123 mkdir("b/c"), 124 rename("a", "b/c/a"), 125 reenableUpdates(), 126 lsdir("a/", m{"b": "DIR"}), 127 lsdir("a/b/", m{"c": "DIR"}), 128 lsdir("a/b/c", m{"a": "SYM"}), 129 lsdir("a/b/c/a", m{"b": "DIR"}), 130 ), 131 as(alice, 132 lsdir("a/", m{"b": "DIR"}), 133 lsdir("a/b/", m{"c": "DIR"}), 134 lsdir("a/b/c", m{"a": "SYM"}), 135 lsdir("a/b/c/a", m{"b": "DIR"}), 136 write("a/d", "hello"), 137 ), 138 as(bob, 139 read("a/b/c/a/d", "hello"), 140 ), 141 ) 142 } 143 144 // bob causes a complicated and large rename(cycle while unstaged), 145 func TestCrComplexLargeRenameCycle(t *testing.T) { 146 test(t, 147 users("alice", "bob"), 148 as(alice, 149 mkdir("a/b/c"), 150 mkdir("d/e/f"), 151 ), 152 as(bob, 153 disableUpdates(), 154 ), 155 as(alice, 156 rename("d", "a/b/c/d"), 157 ), 158 as(bob, noSync(), 159 mkdir("d/e/f/g/h/i"), 160 rename("a", "d/e/f/g/h/i/a"), 161 reenableUpdates(), 162 lsdir("a/b/c/d/e/f/g/h/i", m{"a": "SYM"}), 163 lsdir("a/b/c/d/e/f/g/h/i/a", m{"b": "DIR"}), 164 ), 165 as(alice, 166 lsdir("a/b/c/d/e/f/g/h/i", m{"a": "SYM"}), 167 lsdir("a/b/c/d/e/f/g/h/i/a", m{"b": "DIR"}), 168 write("a/j", "hello"), 169 ), 170 as(bob, 171 read("a/b/c/d/e/f/g/h/i/a/j", "hello"), 172 ), 173 ) 174 } 175 176 // bob and alice do a lot of complex renames cycle while unstaged 177 func TestCrComplexRenameNoCycle(t *testing.T) { 178 test(t, 179 users("alice", "bob"), 180 as(alice, 181 mkdir("a/b/c/d/e/f/g"), 182 ), 183 as(bob, 184 disableUpdates(), 185 ), 186 as(alice, 187 rename("a/b/c/d/e/f", "f"), 188 rename("a/b/c/d", "f/g/d"), 189 rename("a/b", "f/g/d/e/b"), 190 ), 191 as(bob, noSync(), 192 rename("a/b/c/d/e/f/g", "g"), 193 rename("a/b/c/d/e", "g/e"), 194 rename("a/b/c", "g/e/f/c"), 195 rename("a", "g/e/f/c/d/a"), 196 reenableUpdates(), 197 lsdir("f", m{"c": "DIR"}), 198 lsdir("f/c", m{}), 199 lsdir("g", m{"e": "DIR", "d": "DIR"}), 200 lsdir("g/e", m{"b": "DIR"}), 201 lsdir("g/e/b", m{}), 202 lsdir("g/d", m{"a": "DIR"}), 203 ), 204 as(alice, 205 lsdir("f", m{"c": "DIR"}), 206 lsdir("f/c", m{}), 207 lsdir("g", m{"e": "DIR", "d": "DIR"}), 208 lsdir("g/e", m{"b": "DIR"}), 209 lsdir("g/e/b", m{}), 210 lsdir("g/d", m{"a": "DIR"}), 211 ), 212 ) 213 } 214 215 // bob renames a file while unmerged, at the same time alice writes to it 216 func TestCrUnmergedRenameWithParallelWrite(t *testing.T) { 217 test(t, 218 users("alice", "bob"), 219 as(alice, 220 mkdir("a"), 221 mkdir("b"), 222 write("a/foo", "hello"), 223 ), 224 as(bob, 225 disableUpdates(), 226 ), 227 as(alice, 228 write("a/foo", "goodbye"), 229 ), 230 as(bob, noSync(), 231 rename("a/foo", "b/bar"), 232 reenableUpdates(), 233 lsdir("a", m{}), 234 lsdir("b", m{"bar": "FILE"}), 235 read("b/bar", "goodbye"), 236 ), 237 as(alice, 238 lsdir("a", m{}), 239 lsdir("b", m{"bar": "FILE"}), 240 read("b/bar", "goodbye"), 241 ), 242 ) 243 } 244 245 // bob makes a non-conflicting file executable while alice writes to it 246 func TestCrUnmergedSetexParallelWrite(t *testing.T) { 247 test(t, 248 users("alice", "bob"), 249 as(alice, 250 mkfile("a/b", "hello"), 251 ), 252 as(bob, 253 disableUpdates(), 254 ), 255 as(alice, 256 write("a/b", "goodbye"), 257 ), 258 as(bob, noSync(), 259 setex("a/b", true), 260 reenableUpdates(), 261 lsdir("a/", m{"b": "EXEC"}), 262 read("a/b", "goodbye"), 263 ), 264 as(alice, 265 lsdir("a/", m{"b": "EXEC"}), 266 read("a/b", "goodbye"), 267 ), 268 ) 269 } 270 271 // alice makes a non-conflicting file executable while bob writes to it 272 func TestCrMergedSetexParallelWrite(t *testing.T) { 273 test(t, 274 users("alice", "bob"), 275 as(alice, 276 mkfile("a/b", "hello"), 277 ), 278 as(bob, 279 disableUpdates(), 280 ), 281 as(alice, 282 setex("a/b", true), 283 ), 284 as(bob, noSync(), 285 write("a/b", "goodbye"), 286 reenableUpdates(), 287 lsdir("a/", m{"b": "EXEC"}), 288 read("a/b", "goodbye"), 289 ), 290 as(alice, 291 lsdir("a/", m{"b": "EXEC"}), 292 read("a/b", "goodbye"), 293 ), 294 ) 295 } 296 297 // bob writes to a file while alice removes it 298 func TestCrUnmergedWriteToRemovedFile(t *testing.T) { 299 test(t, 300 users("alice", "bob"), 301 as(alice, 302 mkfile("a/b", "hello"), 303 ), 304 as(bob, 305 disableUpdates(), 306 ), 307 as(alice, 308 rm("a/b"), 309 ), 310 as(bob, noSync(), 311 write("a/b", "goodbye"), 312 reenableUpdates(), 313 lsdir("a/", m{"b": "FILE"}), 314 read("a/b", "goodbye"), 315 ), 316 as(alice, 317 lsdir("a/", m{"b": "FILE"}), 318 read("a/b", "goodbye"), 319 ), 320 ) 321 } 322 323 // bob writes to a file while alice renamed then removes it 324 func TestCrUnmergedWriteToRenamedAndRemovedFile(t *testing.T) { 325 test(t, 326 users("alice", "bob"), 327 as(alice, 328 mkfile("a/b/c", "hello"), 329 ), 330 as(bob, 331 disableUpdates(), 332 ), 333 as(alice, 334 mkdir("d"), 335 rename("a/b/c", "d/c"), 336 rm("d/c"), 337 ), 338 as(bob, noSync(), 339 write("a/b/c", "goodbye"), 340 reenableUpdates(), 341 lsdir("", m{"a": "DIR", "d": "DIR"}), 342 lsdir("a/", m{"b": "DIR"}), 343 lsdir("a/b/", m{"c": "FILE"}), 344 read("a/b/c", "goodbye"), 345 lsdir("d/", m{}), 346 ), 347 as(alice, 348 lsdir("", m{"a": "DIR", "d": "DIR"}), 349 lsdir("a/", m{"b": "DIR"}), 350 lsdir("a/b/", m{"c": "FILE"}), 351 read("a/b/c", "goodbye"), 352 lsdir("d/", m{}), 353 ), 354 ) 355 } 356 357 // bob removes a file while alice writes to it 358 func TestCrMergedWriteToRemovedFile(t *testing.T) { 359 test(t, 360 users("alice", "bob"), 361 as(alice, 362 mkfile("a/b", "hello"), 363 ), 364 as(bob, 365 disableUpdates(), 366 ), 367 as(alice, 368 write("a/b", "goodbye"), 369 ), 370 as(bob, noSync(), 371 rm("a/b"), 372 reenableUpdates(), 373 lsdir("a/", m{"b": "FILE"}), 374 read("a/b", "goodbye"), 375 ), 376 as(alice, 377 lsdir("a/", m{"b": "FILE"}), 378 read("a/b", "goodbye"), 379 ), 380 ) 381 } 382 383 // bob writes to a file to a directory that alice removes 384 func TestCrUnmergedCreateInRemovedDir(t *testing.T) { 385 test(t, 386 users("alice", "bob"), 387 as(alice, 388 mkfile("a/b/c/d/e", "hello"), 389 ), 390 as(bob, 391 disableUpdates(), 392 ), 393 as(alice, 394 rm("a/b/c/d/e"), 395 rmdir("a/b/c/d"), 396 rmdir("a/b/c"), 397 rmdir("a/b"), 398 ), 399 as(bob, noSync(), 400 write("a/b/c/d/f", "goodbye"), 401 reenableUpdates(), 402 lsdir("a/b/c/d", m{"f": "FILE"}), 403 read("a/b/c/d/f", "goodbye"), 404 ), 405 as(alice, 406 lsdir("a/b/c/d", m{"f": "FILE"}), 407 read("a/b/c/d/f", "goodbye"), 408 ), 409 ) 410 } 411 412 // alice writes to a file to a directory that bob removes 413 func TestCrMergedCreateInRemovedDir(t *testing.T) { 414 test(t, 415 users("alice", "bob"), 416 as(alice, 417 mkfile("a/b/c/d/e", "hello"), 418 ), 419 as(bob, 420 disableUpdates(), 421 ), 422 as(alice, 423 write("a/b/c/d/f", "goodbye"), 424 ), 425 as(bob, noSync(), 426 rm("a/b/c/d/e"), 427 rmdir("a/b/c/d"), 428 rmdir("a/b/c"), 429 rmdir("a/b"), 430 reenableUpdates(), 431 lsdir("a/b/c/d", m{"f": "FILE"}), 432 read("a/b/c/d/f", "goodbye"), 433 ), 434 as(alice, 435 lsdir("a/b/c/d", m{"f": "FILE"}), 436 read("a/b/c/d/f", "goodbye"), 437 ), 438 ) 439 } 440 441 // bob writes a file while unmerged, at the same time alice renames it 442 func TestCrMergedRenameWithParallelWrite(t *testing.T) { 443 test(t, 444 users("alice", "bob"), 445 as(alice, 446 mkdir("a"), 447 mkdir("b"), 448 write("a/foo", "hello"), 449 ), 450 as(bob, 451 disableUpdates(), 452 ), 453 as(alice, 454 rename("a/foo", "b/bar"), 455 ), 456 as(bob, noSync(), 457 write("a/foo", "goodbye"), 458 reenableUpdates(), 459 lsdir("a", m{}), 460 lsdir("b", m{"bar": "FILE"}), 461 read("b/bar", "goodbye"), 462 ), 463 as(alice, 464 lsdir("a", m{}), 465 lsdir("b", m{"bar": "FILE"}), 466 read("b/bar", "goodbye"), 467 ), 468 ) 469 } 470 471 // bob has two back-to-back resolutions 472 func testCrDoubleResolution(t *testing.T, bSize int64) { 473 test(t, blockSize(bSize), 474 users("alice", "bob"), 475 as(alice, 476 mkdir("a/b"), 477 ), 478 as(bob, 479 disableUpdates(), 480 ), 481 as(alice, 482 write("a/b/c", "hello"), 483 ), 484 as(bob, noSync(), 485 write("a/b/d", "goodbye"), 486 reenableUpdates(), 487 lsdir("a", m{"b": "DIR"}), 488 lsdir("a/b", m{"c": "FILE", "d": "FILE"}), 489 read("a/b/c", "hello"), 490 read("a/b/d", "goodbye"), 491 ), 492 as(alice, 493 lsdir("a", m{"b": "DIR"}), 494 lsdir("a/b", m{"c": "FILE", "d": "FILE"}), 495 read("a/b/c", "hello"), 496 read("a/b/d", "goodbye"), 497 // Make a few more revisions 498 write("a/b/e", "hello"), 499 write("a/b/f", "goodbye"), 500 ), 501 as(bob, 502 read("a/b/e", "hello"), 503 read("a/b/f", "goodbye"), 504 disableUpdates(), 505 ), 506 as(alice, 507 rm("a/b/f"), 508 ), 509 as(bob, noSync(), 510 rm("a/b/e"), 511 reenableUpdates(), 512 lsdir("a", m{"b": "DIR"}), 513 lsdir("a/b", m{"c": "FILE", "d": "FILE"}), 514 read("a/b/c", "hello"), 515 read("a/b/d", "goodbye"), 516 ), 517 as(alice, 518 lsdir("a", m{"b": "DIR"}), 519 lsdir("a/b", m{"c": "FILE", "d": "FILE"}), 520 read("a/b/c", "hello"), 521 read("a/b/d", "goodbye"), 522 ), 523 ) 524 } 525 526 func TestCrDoubleResolution(t *testing.T) { 527 testCrDoubleResolution(t, 0) 528 } 529 530 // Charlie has a resolution that touches a subdirectory that has been 531 // deleted in Bob's resolution. 532 func TestCrDoubleResolutionRmTree(t *testing.T) { 533 test(t, 534 users("alice", "bob", "charlie"), 535 as(alice, 536 write("a/b/c/d/e", "test1"), 537 write("a/b/c/d/f", "test2"), 538 ), 539 as(bob, 540 disableUpdates(), 541 ), 542 as(charlie, 543 disableUpdates(), 544 ), 545 as(alice, 546 write("g", "hello"), 547 ), 548 as(bob, noSync(), 549 // Remove a tree of files. 550 rm("a/b/c/d/e"), 551 rm("a/b/c/d/f"), 552 rm("a/b/c/d"), 553 rm("a/b/c"), 554 reenableUpdates(), 555 lsdir("", m{"a": "DIR", "g": "FILE"}), 556 lsdir("a", m{"b": "DIR"}), 557 lsdir("a/b", m{}), 558 read("g", "hello"), 559 ), 560 as(alice, 561 lsdir("", m{"a": "DIR", "g": "FILE"}), 562 lsdir("a", m{"b": "DIR"}), 563 lsdir("a/b", m{}), 564 read("g", "hello"), 565 ), 566 as(charlie, noSync(), 567 // Touch a subdirectory that was removed by bob. 568 // Unfortunately even though these are just rmOps, they 569 // still re-create "c/d". Tracking a fix for that in 570 // KBFS-1423. 571 rm("a/b/c/d/e"), 572 rm("a/b/c/d/f"), 573 reenableUpdates(), 574 lsdir("", m{"a": "DIR", "g": "FILE"}), 575 lsdir("a", m{"b": "DIR"}), 576 lsdir("a/b", m{"c": "DIR"}), 577 lsdir("a/b/c", m{"d": "DIR"}), 578 lsdir("a/b/c/d", m{}), 579 read("g", "hello"), 580 ), 581 as(alice, 582 lsdir("", m{"a": "DIR", "g": "FILE"}), 583 lsdir("a", m{"b": "DIR"}), 584 lsdir("a/b", m{"c": "DIR"}), 585 lsdir("a/b/c", m{"d": "DIR"}), 586 lsdir("a/b/c/d", m{}), 587 read("g", "hello"), 588 ), 589 as(bob, 590 lsdir("", m{"a": "DIR", "g": "FILE"}), 591 lsdir("a", m{"b": "DIR"}), 592 lsdir("a/b", m{"c": "DIR"}), 593 lsdir("a/b/c", m{"d": "DIR"}), 594 lsdir("a/b/c/d", m{}), 595 read("g", "hello"), 596 ), 597 ) 598 } 599 600 // bob makes files in a directory renamed by alice 601 func TestCrUnmergedMakeFilesInRenamedDir(t *testing.T) { 602 test(t, 603 users("alice", "bob"), 604 as(alice, 605 mkdir("a/b"), 606 ), 607 as(bob, 608 disableUpdates(), 609 ), 610 as(alice, 611 rename("a/b", "b"), 612 ), 613 as(bob, noSync(), 614 write("a/b/c", "hello"), 615 write("a/b/d", "goodbye"), 616 reenableUpdates(), 617 lsdir("a", m{}), 618 lsdir("b", m{"c": "FILE", "d": "FILE"}), 619 read("b/c", "hello"), 620 read("b/d", "goodbye"), 621 ), 622 as(alice, 623 lsdir("a", m{}), 624 lsdir("b", m{"c": "FILE", "d": "FILE"}), 625 read("b/c", "hello"), 626 read("b/d", "goodbye"), 627 ), 628 ) 629 } 630 631 // bob makes files in a directory renamed by alice 632 func TestCrMergedMakeFilesInRenamedDir(t *testing.T) { 633 test(t, 634 users("alice", "bob"), 635 as(alice, 636 mkdir("a/b"), 637 ), 638 as(bob, 639 disableUpdates(), 640 ), 641 as(alice, 642 write("a/b/c", "hello"), 643 write("a/b/d", "goodbye"), 644 ), 645 as(bob, noSync(), 646 rename("a/b", "b"), 647 reenableUpdates(), 648 lsdir("a", m{}), 649 lsdir("b", m{"c": "FILE", "d": "FILE"}), 650 read("b/c", "hello"), 651 read("b/d", "goodbye"), 652 ), 653 as(alice, 654 lsdir("a", m{}), 655 lsdir("b", m{"c": "FILE", "d": "FILE"}), 656 read("b/c", "hello"), 657 read("b/d", "goodbye"), 658 ), 659 ) 660 } 661 662 // bob moves and setexes a file that was written by alice 663 func TestCrConflictMoveAndSetexWrittenFile(t *testing.T) { 664 test(t, 665 users("alice", "bob"), 666 as(alice, 667 mkdir("a"), 668 write("a/b", "hello"), 669 ), 670 as(bob, 671 disableUpdates(), 672 ), 673 as(alice, 674 write("a/b", "world"), 675 ), 676 as(bob, noSync(), 677 rename("a/b", "a/c"), 678 setex("a/c", true), 679 reenableUpdates(), 680 lsdir("a/", m{"c$": "EXEC"}), 681 read("a/c", "world"), 682 ), 683 as(alice, 684 lsdir("a/", m{"c$": "EXEC"}), 685 read("a/c", "world"), 686 ), 687 ) 688 } 689 690 // bob moves and setexes a file that was removed by alice 691 func TestCrConflictMoveAndSetexRemovedFile(t *testing.T) { 692 test(t, 693 users("alice", "bob"), 694 as(alice, 695 mkdir("a"), 696 write("a/b", "hello"), 697 ), 698 as(bob, 699 disableUpdates(), 700 ), 701 as(alice, 702 rm("a/b"), 703 ), 704 as(bob, noSync(), 705 rename("a/b", "a/c"), 706 setex("a/c", true), 707 reenableUpdates(), 708 lsdir("a/", m{"c$": "EXEC"}), 709 read("a/c", "hello"), 710 ), 711 as(alice, 712 lsdir("a/", m{"c$": "EXEC"}), 713 read("a/c", "hello"), 714 ), 715 ) 716 } 717 718 // bob creates a directory with the same name that alice used for a 719 // file that used to exist at that location, but bob first moved it 720 func TestCrMergedRecreatedAndUnmergedMovedFile(t *testing.T) { 721 test(t, 722 users("alice", "bob"), 723 as(alice, 724 mkdir("a"), 725 write("a/b", "hello"), 726 ), 727 as(bob, 728 disableUpdates(), 729 ), 730 as(alice, 731 write("a/b", "world"), 732 ), 733 as(bob, noSync(), 734 rename("a/b", "a/d/b"), 735 rm("a/d/b"), 736 write("a/d/b/c", "uh oh"), 737 reenableUpdates(), 738 lsdir("a/", m{"d$": "DIR", "b$": "FILE"}), 739 lsdir("a/d", m{"b$": "DIR"}), 740 read("a/b", "world"), 741 read("a/d/b/c", "uh oh"), 742 ), 743 as(alice, 744 lsdir("a/", m{"d$": "DIR", "b$": "FILE"}), 745 lsdir("a/d", m{"b$": "DIR"}), 746 read("a/b", "world"), 747 read("a/d/b/c", "uh oh"), 748 ), 749 ) 750 } 751 752 func TestCrUnmergedCreateFileInRenamedDir(t *testing.T) { 753 test(t, 754 users("alice", "bob"), 755 as(alice, 756 mkdir("a/b"), 757 ), 758 as(bob, 759 disableUpdates(), 760 ), 761 as(alice, 762 write("a/c", "touch"), 763 ), 764 as(bob, noSync(), 765 mkdir("a/d"), 766 rename("a/b", "a/d/e"), 767 write("a/d/e/f", "hello"), 768 reenableUpdates(), 769 lsdir("a/", m{"c": "FILE", "d": "DIR"}), 770 lsdir("a/d/", m{"e": "DIR"}), 771 lsdir("a/d/e", m{"f": "FILE"}), 772 read("a/d/e/f", "hello"), 773 ), 774 as(alice, 775 lsdir("a/", m{"c": "FILE", "d": "DIR"}), 776 lsdir("a/d/", m{"e": "DIR"}), 777 lsdir("a/d/e", m{"f": "FILE"}), 778 read("a/d/e/f", "hello"), 779 ), 780 ) 781 } 782 783 // bob moves a file that was removed by alice 784 func TestCrUnmergedMoveOfRemovedFile(t *testing.T) { 785 test(t, 786 users("alice", "bob"), 787 as(alice, 788 mkdir("a"), 789 write("a/b", "hello"), 790 ), 791 as(bob, 792 disableUpdates(), 793 ), 794 as(alice, 795 rm("a/b"), 796 ), 797 as(bob, noSync(), 798 rename("a/b", "a/c"), 799 reenableUpdates(), 800 lsdir("a/", m{"c$": "FILE"}), 801 read("a/c", "hello"), 802 ), 803 as(alice, 804 lsdir("a/", m{"c$": "FILE"}), 805 read("a/c", "hello"), 806 ), 807 ) 808 } 809 810 // bob makes, sets the mtime, and remove a file. Regression test for 811 // KBFS-1163. 812 func TestCrUnmergedSetMtimeOfRemovedDir(t *testing.T) { 813 targetMtime := time.Now().Add(1 * time.Minute) 814 test(t, 815 users("alice", "bob"), 816 as(alice, 817 mkdir("a/b/c"), 818 mkfile("a/b/c/d", "hello"), 819 ), 820 as(bob, 821 disableUpdates(), 822 ), 823 as(alice, 824 rm("a/b/c/d"), 825 rm("a/b/c"), 826 rm("a/b"), 827 rm("a"), 828 ), 829 as(bob, noSync(), 830 setmtime("a/b/c", targetMtime), 831 mkfile("e", "world"), 832 reenableUpdates(), 833 lsdir("", m{"a$": "DIR", "e$": "FILE"}), 834 lsdir("a", m{"b$": "DIR"}), 835 lsdir("a/b", m{"c$": "DIR"}), 836 lsdir("a/b/c", m{}), 837 mtime("a/b/c", targetMtime), 838 read("e", "world"), 839 ), 840 as(alice, 841 lsdir("", m{"a$": "DIR", "e$": "FILE"}), 842 lsdir("a", m{"b$": "DIR"}), 843 lsdir("a/b", m{"c$": "DIR"}), 844 lsdir("a/b/c", m{}), 845 mtime("a/b/c", targetMtime), 846 read("e", "world"), 847 ), 848 ) 849 } 850 851 // bob sets the mtime of a dir that is also modified by alice, then 852 // removes that dir. Regression test for KBFS-1691. 853 func TestCrUnmergedSetMtimeAndRemoveModifiedDir(t *testing.T) { 854 origMtime := time.Now().Add(1 * time.Minute) 855 targetMtime := time.Now().Add(2 * time.Minute) 856 test(t, 857 users("alice", "bob"), 858 as(alice, 859 mkdir("a/b/c"), 860 mkfile("a/b/c/d", "hello"), 861 setmtime("a/b/c", origMtime), 862 setmtime("a/b", origMtime), 863 ), 864 as(bob, 865 disableUpdates(), 866 ), 867 as(alice, 868 mkfile("a/b/c/e", "hello2"), 869 mkfile("a/b/f", "hello3"), 870 setmtime("a/b/c", origMtime), 871 setmtime("a/b", origMtime), 872 ), 873 as(bob, noSync(), 874 setmtime("a/b/c", targetMtime), 875 setmtime("a/b", targetMtime), 876 rm("a/b/c/d"), 877 rmdir("a/b/c"), 878 rmdir("a/b"), 879 reenableUpdates(), 880 lsdir("", m{"a$": "DIR"}), 881 lsdir("a", m{"b$": "DIR"}), 882 lsdir("a/b", m{"c$": "DIR", "f$": "FILE"}), 883 mtime("a/b", origMtime), 884 lsdir("a/b/c", m{"e$": "FILE"}), 885 mtime("a/b/c", origMtime), 886 read("a/b/c/e", "hello2"), 887 read("a/b/f", "hello3"), 888 ), 889 as(alice, 890 lsdir("", m{"a$": "DIR"}), 891 lsdir("a", m{"b$": "DIR"}), 892 lsdir("a/b", m{"c$": "DIR", "f$": "FILE"}), 893 mtime("a/b", origMtime), 894 lsdir("a/b/c", m{"e$": "FILE"}), 895 mtime("a/b/c", origMtime), 896 read("a/b/c/e", "hello2"), 897 read("a/b/f", "hello3"), 898 ), 899 ) 900 } 901 902 // bob moves and sets the mtime of a file that was written by alice 903 func TestCrConflictMoveAndSetMtimeWrittenFile(t *testing.T) { 904 targetMtime := time.Now().Add(1 * time.Minute) 905 test(t, 906 users("alice", "bob"), 907 as(alice, 908 mkdir("a"), 909 write("a/b", "hello"), 910 ), 911 as(bob, 912 disableUpdates(), 913 ), 914 as(alice, 915 write("a/b", "world"), 916 ), 917 as(bob, noSync(), 918 rename("a/b", "a/c"), 919 setmtime("a/c", targetMtime), 920 reenableUpdates(), 921 lsdir("a/", m{"c$": "FILE"}), 922 read("a/c", "world"), 923 mtime("a/c", targetMtime), 924 ), 925 as(alice, 926 lsdir("a/", m{"c$": "FILE"}), 927 read("a/c", "world"), 928 mtime("a/c", targetMtime), 929 ), 930 ) 931 } 932 933 // bob moves and sets the mtime of a file while conflicted, and then 934 // charlie resolves a conflict on top of bob's resolution. This is a 935 // regression test for KBFS-1534. 936 func TestCrConflictWriteMoveAndSetMtimeFollowedByNewConflict(t *testing.T) { 937 targetMtime := time.Now().Add(1 * time.Minute) 938 test(t, 939 users("alice", "bob", "charlie"), 940 as(alice, 941 mkdir("a"), 942 ), 943 as(bob, 944 write("a/b", "hello"), 945 disableUpdates(), 946 ), 947 as(charlie, 948 disableUpdates(), 949 ), 950 as(alice, 951 write("a/c", "hello"), 952 ), 953 as(bob, noSync(), 954 write("a/b", "hello world"), 955 setmtime("a/b", targetMtime), 956 rename("a/b", "a/d"), 957 reenableUpdates(), 958 lsdir("a/", m{"c$": "FILE", "d$": "FILE"}), 959 read("a/c", "hello"), 960 read("a/d", "hello world"), 961 mtime("a/d", targetMtime), 962 ), 963 as(charlie, noSync(), 964 write("a/e", "hello too"), 965 reenableUpdates(), 966 lsdir("a/", m{"c$": "FILE", "d$": "FILE", "e$": "FILE"}), 967 read("a/c", "hello"), 968 read("a/d", "hello world"), 969 mtime("a/d", targetMtime), 970 read("a/e", "hello too"), 971 ), 972 as(alice, 973 lsdir("a/", m{"c$": "FILE", "d$": "FILE", "e$": "FILE"}), 974 read("a/c", "hello"), 975 read("a/d", "hello world"), 976 mtime("a/d", targetMtime), 977 read("a/e", "hello too"), 978 ), 979 ) 980 } 981 982 // Regression test for keybase/client#9034. 983 func TestCrCreateFileSetmtimeRenameRemoveUnmerged(t *testing.T) { 984 test(t, 985 users("alice", "bob"), 986 as(alice, 987 mkdir("a"), 988 write("a/b", "hello"), 989 ), 990 as(bob, 991 disableUpdates(), 992 ), 993 as(alice, 994 rm("a/b"), 995 ), 996 as(bob, noSync(), 997 mkfile("c", ""), 998 pwriteBSSync("c", []byte("test"), 0, false), 999 setmtime("c", time.Now()), 1000 rename("c", "d"), 1001 rm("d"), 1002 reenableUpdates(), 1003 lsdir("a/", m{}), 1004 ), 1005 as(alice, 1006 lsdir("a/", m{}), 1007 ), 1008 ) 1009 } 1010 1011 // Regression test for keybase/client#9034. 1012 func TestCrJournalCreateDirRenameFileRemoveUnmerged(t *testing.T) { 1013 test(t, journal(), 1014 users("alice", "bob"), 1015 as(alice, 1016 mkdir("a"), 1017 write("a/b", "hello"), 1018 ), 1019 as(bob, 1020 enableJournal(), 1021 pauseJournal(), 1022 mkdir("x"), 1023 ), 1024 as(alice, 1025 rm("a/b"), 1026 ), 1027 as(bob, 1028 mkdir("c"), 1029 mkfile("c/d", ""), 1030 pwriteBSSync("c/d", []byte("test"), 0, false), 1031 rename("c/d", "c/e"), 1032 rm("c/e"), 1033 ), 1034 as(bob, 1035 rmdir("c"), 1036 ), 1037 as(bob, 1038 resumeJournal(), 1039 flushJournal(), 1040 ), 1041 as(bob, 1042 lsdir("a/", m{}), 1043 lsdir("", m{"a$": "DIR", "x$": "DIR"}), 1044 ), 1045 as(alice, 1046 lsdir("a/", m{}), 1047 lsdir("", m{"a$": "DIR", "x$": "DIR"}), 1048 ), 1049 ) 1050 } 1051 1052 // Regression test for KBFS-2915. 1053 func TestCrDoubleMergedDeleteAndRecreate(t *testing.T) { 1054 test(t, 1055 users("alice", "bob"), 1056 as(alice, 1057 mkdir("a/b/c/d"), 1058 write("a/b/c/d/e1/f1", "f1"), 1059 write("a/b/c/d/e2/f2", "f2"), 1060 ), 1061 as(bob, 1062 disableUpdates(), 1063 ), 1064 as(alice, 1065 rm("a/b/c/d/e1/f1"), 1066 rm("a/b/c/d/e2/f2"), 1067 rmdir("a/b/c/d/e1"), 1068 rmdir("a/b/c/d/e2"), 1069 rmdir("a/b/c/d"), 1070 rmdir("a/b/c"), 1071 ), 1072 as(bob, noSync(), 1073 write("a/b/c/d/e1/f1", "f1.2"), 1074 write("a/b/c/d/e2/f2", "f2.2"), 1075 reenableUpdates(), 1076 read("a/b/c/d/e1/f1", "f1.2"), 1077 read("a/b/c/d/e2/f2", "f2.2"), 1078 ), 1079 as(alice, 1080 read("a/b/c/d/e1/f1", "f1.2"), 1081 read("a/b/c/d/e2/f2", "f2.2"), 1082 ), 1083 ) 1084 } 1085 1086 // Regression test for KBFS-3915. 1087 func TestCrSetMtimeOnCreatedDir(t *testing.T) { 1088 targetMtime1 := time.Now().Add(1 * time.Minute) 1089 targetMtime2 := targetMtime1.Add(1 * time.Minute) 1090 test(t, batchSize(1), 1091 users("alice", "bob"), 1092 as(alice, 1093 mkdir("a"), 1094 ), 1095 as(bob, 1096 disableUpdates(), 1097 ), 1098 as(alice, 1099 mkdir("a/b/c"), 1100 setmtime("a/b", targetMtime1), 1101 ), 1102 as(bob, noSync(), 1103 mkdir("a/b/c"), 1104 setmtime("a/b", targetMtime2), 1105 reenableUpdates(), 1106 mtime("a/b", targetMtime1), 1107 mtime(crname("a/b", bob), targetMtime2), 1108 ), 1109 as(alice, 1110 mtime("a/b", targetMtime1), 1111 mtime(crname("a/b", bob), targetMtime2), 1112 ), 1113 ) 1114 } 1115 1116 // bob sets the mtime of a file and moves it over a file that had its 1117 // mtime set by alice. Regression test for HOTPOT-719. 1118 func TestCrConflictSetMtimeOnRenamedOverFile(t *testing.T) { 1119 targetMtime1 := time.Now().Add(1 * time.Minute) 1120 targetMtime2 := time.Now().Add(2 * time.Minute) 1121 test(t, 1122 users("alice", "bob"), 1123 as(alice, 1124 mkdir("a"), 1125 write("a/b", "hello"), 1126 write("a/c", "world"), 1127 ), 1128 as(bob, 1129 disableUpdates(), 1130 ), 1131 as(alice, 1132 setmtime("a/b", targetMtime1), 1133 ), 1134 as(bob, noSync(), 1135 setmtime("a/b", targetMtime1), 1136 write("a/c", "world2"), 1137 setmtime("a/c", targetMtime2), 1138 rename("a/c", "a/b"), 1139 reenableUpdates(), 1140 mtime("a/b", targetMtime1), 1141 mtime(crname("a/b", bob), targetMtime2), 1142 read(crname("a/b", bob), "world2"), 1143 ), 1144 as(alice, 1145 mtime("a/b", targetMtime1), 1146 mtime(crname("a/b", bob), targetMtime2), 1147 read(crname("a/b", bob), "world2"), 1148 ), 1149 ) 1150 }