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