From 77881a9c7d24bb11bc74abff35d94397fd4ccb67 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sun, 7 Aug 2022 22:09:39 +1000 Subject: [PATCH 01/16] add new integration test pattern --- .gitignore | 10 +- main.go | 5 + pkg/gui/assert.go | 88 +++ pkg/gui/gui.go | 31 +- pkg/gui/gui_test.go | 34 +- pkg/gui/input.go | 93 +++ pkg/gui/old_gui_test.go | 76 +++ pkg/gui/test_mode.go | 63 ++ pkg/integration/env.go | 46 ++ pkg/integration/integration.go | 529 +++------------- pkg/integration/integration_old.go | 564 ++++++++++++++++++ .../integration_tests/branch/suggestions.go | 40 ++ .../integration_tests/commit/commit.go | 32 + .../integration_tests/commit/new_branch.go | 38 ++ pkg/integration/integration_tests/tests.go | 16 + pkg/{gui => integration}/recording.go | 27 +- pkg/integration/shell.go | 53 ++ pkg/integration/types/types.go | 152 +++++ pkg/utils/utils.go | 7 + .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 - .../expected/repo/.git_keep/HEAD | 1 - .../expected/repo/.git_keep/index | Bin 137 -> 0 bytes .../expected/repo/.git_keep/logs/HEAD | 8 - .../repo/.git_keep/logs/refs/heads/master | 1 - .../repo/.git_keep/logs/refs/heads/new-branch | 1 - .../.git_keep/logs/refs/heads/new-branch-2 | 1 - .../.git_keep/logs/refs/heads/new-branch-3 | 1 - .../repo/.git_keep/logs/refs/heads/old-branch | 1 - .../.git_keep/logs/refs/heads/old-branch-2 | 1 - .../.git_keep/logs/refs/heads/old-branch-3 | 1 - .../1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 | Bin 50 -> 0 bytes .../38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da | Bin 21 -> 0 bytes .../75/e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 | 2 - .../expected/repo/.git_keep/refs/heads/master | 1 - .../repo/.git_keep/refs/heads/new-branch | 1 - .../repo/.git_keep/refs/heads/new-branch-2 | 1 - .../repo/.git_keep/refs/heads/new-branch-3 | 1 - .../repo/.git_keep/refs/heads/old-branch | 1 - .../repo/.git_keep/refs/heads/old-branch-2 | 1 - .../repo/.git_keep/refs/heads/old-branch-3 | 1 - .../branchSuggestions/expected/repo/file0 | 1 - .../branchSuggestions/recording.json | 1 - test/integration/branchSuggestions/setup.sh | 21 - test/integration/branchSuggestions/test.json | 1 - .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 - .../commit/expected/repo/.git_keep/index | Bin 425 -> 0 bytes .../commit/expected/repo/.git_keep/logs/HEAD | 5 - .../repo/.git_keep/logs/refs/heads/master | 5 - .../0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 | Bin 52 -> 0 bytes .../14/40bc6cc888a09dca2329d1060eec6de78d9d21 | Bin 148 -> 0 bytes .../18/0cf8328022becee9aaa2577a8f84ea2b9f3827 | Bin 21 -> 0 bytes .../2b/173c861df433fa43ffad13f80c8b312c5c8bce | Bin 103 -> 0 bytes .../2f/6174050380438f14b16658a356e762435ca591 | Bin 128 -> 0 bytes .../30/a1ca3481fdec3245b02aeacfb72ddfe2a433be | Bin 154 -> 0 bytes .../3d/f3d8761bc0f0828596b11845aeac175b7b7393 | 3 - .../4b/a4f1ed711a9081fab21bc222469aa5176a01f8 | Bin 149 -> 0 bytes .../4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f | Bin 21 -> 0 bytes .../a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 | Bin 21 -> 0 bytes .../a7/341a59f0ddeef969e69fb6368266d22b0f2416 | Bin 77 -> 0 bytes .../a7/d53cc21fd53100f955377be379423b0e386274 | Bin 150 -> 0 bytes .../d2/34c5e057fe32c676ea67e8cb38f4625ddaeb54 | Bin 21 -> 0 bytes .../df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b | Bin 21 -> 0 bytes .../e7/560e2cd4783a261ad32496cefed2d9f69a46e7 | Bin 145 -> 0 bytes .../expected/repo/.git_keep/refs/heads/master | 1 - test/integration/commit/expected/repo/myfile1 | 1 - test/integration/commit/expected/repo/myfile2 | 1 - test/integration/commit/expected/repo/myfile3 | 1 - test/integration/commit/expected/repo/myfile4 | 1 - test/integration/commit/expected/repo/myfile5 | 1 - test/integration/commit/recording.json | 1 - test/integration/commit/setup.sh | 24 - test/integration/commit/test.json | 1 - .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 - .../expected/repo/.git_keep/HEAD | 1 - .../expected/repo/.git_keep/index | Bin 209 -> 0 bytes .../expected/repo/.git_keep/logs/HEAD | 4 - .../repo/.git_keep/logs/refs/heads/lol | 1 - .../repo/.git_keep/logs/refs/heads/master | 3 - .../00/29f9bf66e346d47ede6a501abb5b82bee60096 | Bin 148 -> 0 bytes .../18/0cf8328022becee9aaa2577a8f84ea2b9f3827 | Bin 21 -> 0 bytes .../1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 | Bin 50 -> 0 bytes .../38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da | Bin 21 -> 0 bytes .../99/01fd9b7766be600bed07f55f1794a759527a98 | Bin 118 -> 0 bytes .../9e/88a70dc8d82dd2afbfd50176ef78e18823bc2c | Bin 101 -> 0 bytes .../a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 | Bin 21 -> 0 bytes .../d0/76cc9cc09acaa2d36fbc7a95fd3e2306494641 | 2 - .../e1/cb250774fb8606d33062518d0ae03831130249 | Bin 148 -> 0 bytes .../expected/repo/.git_keep/refs/heads/lol | 1 - .../expected/repo/.git_keep/refs/heads/master | 1 - .../commitsNewBranch/expected/repo/file0 | 1 - .../commitsNewBranch/expected/repo/file1 | 1 - .../commitsNewBranch/recording.json | 1 - test/integration/commitsNewBranch/setup.sh | 22 - test/integration/commitsNewBranch/test.json | 1 - .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 + .../expected/repo/.git_keep/FETCH_HEAD | 0 .../suggestions/expected/repo/.git_keep/HEAD | 1 + .../expected/repo/.git_keep/config | 0 .../expected/repo/.git_keep/description | 0 .../suggestions/expected/repo/.git_keep/index | Bin 0 -> 65 bytes .../expected/repo/.git_keep/info/exclude | 0 .../expected/repo/.git_keep/logs/HEAD | 8 + .../logs/refs/heads/branch-to-checkout | 1 + .../repo/.git_keep/logs/refs/heads/master | 1 + .../repo/.git_keep/logs/refs/heads/new-branch | 1 + .../.git_keep/logs/refs/heads/new-branch-2 | 1 + .../.git_keep/logs/refs/heads/new-branch-3 | 1 + .../logs/refs/heads/other-new-branch-2 | 1 + .../logs/refs/heads/other-new-branch-3 | 1 + .../16/82dc1949e1937af44b5270fec5c1ac9256c6a1 | Bin 0 -> 125 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../.git_keep/refs/heads/branch-to-checkout | 1 + .../expected/repo/.git_keep/refs/heads/master | 1 + .../repo/.git_keep/refs/heads/new-branch | 1 + .../repo/.git_keep/refs/heads/new-branch-2 | 1 + .../repo/.git_keep/refs/heads/new-branch-3 | 1 + .../.git_keep/refs/heads/other-new-branch-2 | 1 + .../.git_keep/refs/heads/other-new-branch-3 | 1 + .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 + .../commit/expected/repo/.git_keep/FETCH_HEAD | 0 .../commit/expected/repo/.git_keep/HEAD | 0 .../commit/expected/repo/.git_keep/config | 0 .../expected/repo/.git_keep/description | 0 .../commit/expected/repo/.git_keep/index | Bin 0 -> 209 bytes .../expected/repo/.git_keep/info/exclude | 0 .../commit/expected/repo/.git_keep/logs/HEAD | 1 + .../repo/.git_keep/logs/refs/heads/master | 1 + .../3a/e2df795236e3c84cb1faa242d3268838603515 | Bin 0 -> 76 bytes .../46/0150760ff1f381c3f5769b919cb73107c5871a | Bin 0 -> 125 bytes .../97/04090f88911a4083ef7d5907e38b9f45e43b16 | Bin 0 -> 31 bytes .../ad/a5661567ddf0a64f589cad3cd0cffd7e79af99 | Bin 0 -> 30 bytes .../expected/repo/.git_keep/refs/heads/master | 1 + .../commit/commit/expected/repo/myfile | 1 + .../commit/commit/expected/repo/myfile2 | 1 + .../expected/repo/.git_keep/COMMIT_EDITMSG | 1 + .../expected/repo/.git_keep/FETCH_HEAD | 0 .../new_branch/expected/repo/.git_keep/HEAD | 1 + .../expected/repo/.git_keep/config | 0 .../expected/repo/.git_keep/description | 0 .../new_branch/expected/repo/.git_keep/index | Bin 0 -> 65 bytes .../expected/repo/.git_keep/info/exclude | 0 .../expected/repo/.git_keep/logs/HEAD | 4 + .../repo/.git_keep/logs/refs/heads/master | 3 + .../.git_keep/logs/refs/heads/my-branch-name | 1 + .../47/0038e1336649b2965305f9f6a82501a836810e | Bin 0 -> 118 bytes .../4b/825dc642cb6eb9a060e54bf8d69288fbee4904 | Bin 0 -> 15 bytes .../62/a60693a2e154e745ee353f67a05156d0532c23 | Bin 0 -> 148 bytes .../c8/bec8f2b323cbb476e708bd10c145ea7cc9f726 | 2 + .../expected/repo/.git_keep/refs/heads/master | 1 + .../repo/.git_keep/refs/heads/my-branch-name | 1 + test/runner/main.go | 2 + test/runner_new/main.go | 76 +++ vendor/github.com/jesseduffield/gocui/gui.go | 16 +- .../jesseduffield/gocui/tcell_driver.go | 6 +- 154 files changed, 1514 insertions(+), 670 deletions(-) create mode 100644 pkg/gui/assert.go create mode 100644 pkg/gui/input.go create mode 100644 pkg/gui/old_gui_test.go create mode 100644 pkg/gui/test_mode.go create mode 100644 pkg/integration/env.go create mode 100644 pkg/integration/integration_old.go create mode 100644 pkg/integration/integration_tests/branch/suggestions.go create mode 100644 pkg/integration/integration_tests/commit/commit.go create mode 100644 pkg/integration/integration_tests/commit/new_branch.go create mode 100644 pkg/integration/integration_tests/tests.go rename pkg/{gui => integration}/recording.go (62%) create mode 100644 pkg/integration/shell.go create mode 100644 pkg/integration/types/types.go delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/COMMIT_EDITMSG delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/HEAD delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/index delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/HEAD delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/master delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-2 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-3 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/objects/75/e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/master delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-2 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-3 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-2 delete mode 100644 test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-3 delete mode 100644 test/integration/branchSuggestions/expected/repo/file0 delete mode 100644 test/integration/branchSuggestions/recording.json delete mode 100644 test/integration/branchSuggestions/setup.sh delete mode 100644 test/integration/branchSuggestions/test.json delete mode 100644 test/integration/commit/expected/repo/.git_keep/COMMIT_EDITMSG delete mode 100644 test/integration/commit/expected/repo/.git_keep/index delete mode 100644 test/integration/commit/expected/repo/.git_keep/logs/HEAD delete mode 100644 test/integration/commit/expected/repo/.git_keep/logs/refs/heads/master delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/14/40bc6cc888a09dca2329d1060eec6de78d9d21 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/2f/6174050380438f14b16658a356e762435ca591 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/30/a1ca3481fdec3245b02aeacfb72ddfe2a433be delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/3d/f3d8761bc0f0828596b11845aeac175b7b7393 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/4b/a4f1ed711a9081fab21bc222469aa5176a01f8 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/a7/341a59f0ddeef969e69fb6368266d22b0f2416 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/a7/d53cc21fd53100f955377be379423b0e386274 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/d2/34c5e057fe32c676ea67e8cb38f4625ddaeb54 delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b delete mode 100644 test/integration/commit/expected/repo/.git_keep/objects/e7/560e2cd4783a261ad32496cefed2d9f69a46e7 delete mode 100644 test/integration/commit/expected/repo/.git_keep/refs/heads/master delete mode 100644 test/integration/commit/expected/repo/myfile1 delete mode 100644 test/integration/commit/expected/repo/myfile2 delete mode 100644 test/integration/commit/expected/repo/myfile3 delete mode 100644 test/integration/commit/expected/repo/myfile4 delete mode 100644 test/integration/commit/expected/repo/myfile5 delete mode 100644 test/integration/commit/recording.json delete mode 100644 test/integration/commit/setup.sh delete mode 100644 test/integration/commit/test.json delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/COMMIT_EDITMSG delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/HEAD delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/index delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/logs/HEAD delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/lol delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/master delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/00/29f9bf66e346d47ede6a501abb5b82bee60096 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/99/01fd9b7766be600bed07f55f1794a759527a98 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/9e/88a70dc8d82dd2afbfd50176ef78e18823bc2c delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/d0/76cc9cc09acaa2d36fbc7a95fd3e2306494641 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/objects/e1/cb250774fb8606d33062518d0ae03831130249 delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/lol delete mode 100644 test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/master delete mode 100644 test/integration/commitsNewBranch/expected/repo/file0 delete mode 100644 test/integration/commitsNewBranch/expected/repo/file1 delete mode 100644 test/integration/commitsNewBranch/recording.json delete mode 100644 test/integration/commitsNewBranch/setup.sh delete mode 100644 test/integration/commitsNewBranch/test.json create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/COMMIT_EDITMSG rename test/{integration/branchSuggestions => integration_new/branch/suggestions}/expected/repo/.git_keep/FETCH_HEAD (100%) create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/HEAD rename test/{integration/branchSuggestions => integration_new/branch/suggestions}/expected/repo/.git_keep/config (100%) rename test/{integration/branchSuggestions => integration_new/branch/suggestions}/expected/repo/.git_keep/description (100%) create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/index rename test/{integration/branchSuggestions => integration_new/branch/suggestions}/expected/repo/.git_keep/info/exclude (100%) create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/HEAD create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/branch-to-checkout create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/master create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-2 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-3 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/16/82dc1949e1937af44b5270fec5c1ac9256c6a1 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/branch-to-checkout create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/master create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/new-branch create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/new-branch-2 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/new-branch-3 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/other-new-branch-2 create mode 100644 test/integration_new/branch/suggestions/expected/repo/.git_keep/refs/heads/other-new-branch-3 create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/COMMIT_EDITMSG rename test/{integration => integration_new/commit}/commit/expected/repo/.git_keep/FETCH_HEAD (100%) rename test/{integration => integration_new/commit}/commit/expected/repo/.git_keep/HEAD (100%) rename test/{integration => integration_new/commit}/commit/expected/repo/.git_keep/config (100%) rename test/{integration => integration_new/commit}/commit/expected/repo/.git_keep/description (100%) create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/index rename test/{integration => integration_new/commit}/commit/expected/repo/.git_keep/info/exclude (100%) create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/logs/HEAD create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/3a/e2df795236e3c84cb1faa242d3268838603515 create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/97/04090f88911a4083ef7d5907e38b9f45e43b16 create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/ad/a5661567ddf0a64f589cad3cd0cffd7e79af99 create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/refs/heads/master create mode 100644 test/integration_new/commit/commit/expected/repo/myfile create mode 100644 test/integration_new/commit/commit/expected/repo/myfile2 create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/COMMIT_EDITMSG rename test/{integration/commitsNewBranch => integration_new/commit/new_branch}/expected/repo/.git_keep/FETCH_HEAD (100%) create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/HEAD rename test/{integration/commitsNewBranch => integration_new/commit/new_branch}/expected/repo/.git_keep/config (100%) rename test/{integration/commitsNewBranch => integration_new/commit/new_branch}/expected/repo/.git_keep/description (100%) create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/index rename test/{integration/commitsNewBranch => integration_new/commit/new_branch}/expected/repo/.git_keep/info/exclude (100%) create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/HEAD create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/62/a60693a2e154e745ee353f67a05156d0532c23 create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/c8/bec8f2b323cbb476e708bd10c145ea7cc9f726 create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name create mode 100644 test/runner_new/main.go diff --git a/.gitignore b/.gitignore index f5ba66f88..e9ed453a2 100644 --- a/.gitignore +++ b/.gitignore @@ -33,11 +33,19 @@ lazygit.exe !.gitmodules_keep test/git_server/data + +# we'll scrap these lines once we've fully moved over to the new integration test approach test/integration/*/actual/ test/integration/*/used_config/ # these sample hooks waste too much space test/integration/*/expected/**/hooks/ test/integration/*/expected_remote/**/hooks/ +test/integration_new/**/actual/ +test/integration_new/**/used_config/ +# these sample hooks waste too much space +test/integration_new/**/expected/**/hooks/ +test/integration_new/**/expected_remote/**/hooks/ + oryxBuildBinary -__debug_bin \ No newline at end of file +__debug_bin diff --git a/main.go b/main.go index e21751c27..651d2be09 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/logs" "github.com/jesseduffield/lazygit/pkg/utils" yaml "github.com/jesseduffield/yaml" @@ -150,6 +151,10 @@ func main() { log.Fatal(err.Error()) } + if test, ok := integration.CurrentIntegrationTest(); ok { + test.SetupConfig(appConfig) + } + common, err := app.NewCommon(appConfig) if err != nil { log.Fatal(err) diff --git a/pkg/gui/assert.go b/pkg/gui/assert.go new file mode 100644 index 000000000..43ecc26d8 --- /dev/null +++ b/pkg/gui/assert.go @@ -0,0 +1,88 @@ +package gui + +import ( + "fmt" + "time" + + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type AssertImpl struct { + gui *Gui +} + +var _ types.Assert = &AssertImpl{} + +func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.State.Model.Files) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d changed working tree files, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) CommitCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.State.Model.Commits) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d commits present, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { + self.assertWithRetries(func() (bool, string) { + if len(self.gui.State.Model.Commits) == 0 { + return false, "Expected at least one commit to be present" + } + + headCommit := self.gui.State.Model.Commits[0] + if headCommit.Name != expectedMessage { + return false, fmt.Sprintf( + "Expected commit message to be '%s', but got '%s'", + expectedMessage, headCommit.Name, + ) + } + + return true, "" + }) +} + +func (self *AssertImpl) CurrentViewName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.currentViewName() + return actual == expectedViewName, fmt.Sprintf("Expected current view name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) CurrentBranchName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.helpers.Refs.GetCheckedOutRef().Name + return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { + waitTimes := []int{0, 100, 200, 400, 800, 1600} + + var message string + for _, waitTime := range waitTimes { + time.Sleep(time.Duration(waitTime) * time.Millisecond) + + var ok bool + ok, message = test() + if ok { + return + } + } + + self.gui.g.Close() + // need to give the gui time to close + time.Sleep(time.Millisecond * 100) + panic(message) +} diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index bb5821a57..f7c8926f5 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -31,6 +31,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/tasks" "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/updates" @@ -418,12 +419,14 @@ var RuneReplacements = map[rune]string{ } func (gui *Gui) initGocui(headless bool) (*gocui.Gui, error) { - recordEvents := recordingEvents() + recordEvents := integration.RecordingEvents() playMode := gocui.NORMAL if recordEvents { playMode = gocui.RECORDING - } else if replaying() { + } else if integration.Replaying() { playMode = gocui.REPLAYING + } else if integration.IntegrationTestName() != "" { + playMode = gocui.REPLAYING_NEW } g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, playMode, headless, RuneReplacements) @@ -475,7 +478,7 @@ func (gui *Gui) viewTabMap() map[string][]context.TabView { // Run: setup the gui with keybindings and start the mainloop func (gui *Gui) Run(startArgs types.StartArgs) error { - g, err := gui.initGocui(headless()) + g, err := gui.initGocui(integration.Headless()) if err != nil { return err } @@ -490,23 +493,7 @@ func (gui *Gui) Run(startArgs types.StartArgs) error { }) deadlock.Opts.Disable = !gui.Debug - if replaying() { - gui.g.RecordingConfig = gocui.RecordingConfig{ - Speed: getRecordingSpeed(), - Leeway: 100, - } - - var err error - gui.g.Recording, err = gui.loadRecording() - if err != nil { - return err - } - - go utils.Safe(func() { - time.Sleep(time.Second * 40) - log.Fatal("40 seconds is up, lazygit recording took too long to complete") - }) - } + gui.handleTestMode() gui.g.OnSearchEscape = gui.onSearchEscape if err := gui.Config.ReloadUserConfig(); err != nil { @@ -593,7 +580,7 @@ func (gui *Gui) RunAndHandleError(startArgs types.StartArgs) error { } } - if err := gui.saveRecording(gui.g.Recording); err != nil { + if err := integration.SaveRecording(gui.g.Recording); err != nil { return err } @@ -627,7 +614,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool, gui.Mutexes.SubprocessMutex.Lock() defer gui.Mutexes.SubprocessMutex.Unlock() - if replaying() { + if integration.Replaying() { // we do not yet support running subprocesses within integration tests. So if // we're replaying an integration test and we're inside this method, something // has gone wrong, so we should fail diff --git a/pkg/gui/gui_test.go b/pkg/gui/gui_test.go index d2345d5d0..2565392e6 100644 --- a/pkg/gui/gui_test.go +++ b/pkg/gui/gui_test.go @@ -3,6 +3,9 @@ package gui +// this is the new way of running tests. See pkg/integration/integration_tests/commit.go +// for an example + import ( "fmt" "io" @@ -14,60 +17,37 @@ import ( "github.com/creack/pty" "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/stretchr/testify/assert" ) -// This file is quite similar to integration/main.go. The main difference is that this file is -// run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides -// a convenient gui wrapper around our integration tests. The `go test` approach is better -// for CI and for running locally in the background to ensure you haven't broken -// anything while making changes. If you want to visually see what's happening when a test is run, -// you'll need to take the other approach -// -// As for this file, to run an integration test, e.g. for test 'commit', go: -// go test pkg/gui/gui_test.go -run /commit -// -// To update a snapshot for an integration test, pass UPDATE_SNAPSHOTS=true -// UPDATE_SNAPSHOTS=true go test pkg/gui/gui_test.go -run /commit -// -// integration tests are run in test/integration//actual and the final test does -// not clean up that directory so you can cd into it to see for yourself what -// happened when a test fails. -// -// To override speed, pass e.g. `SPEED=1` as an env var. Otherwise we start each test -// at a high speed and then drop down to lower speeds upon each failure until finally -// trying at the original playback speed (speed 1). A speed of 2 represents twice the -// original playback speed. Speed may be a decimal. - func Test(t *testing.T) { if testing.Short() { t.Skip("Skipping integration tests in short mode") } mode := integration.GetModeFromEnv() - speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) testNumber := 0 - err := integration.RunTests( + err := integration.RunTestsNew( t.Logf, runCmdHeadless, - func(test *integration.Test, f func(*testing.T) error) { + func(test types.Test, f func(*testing.T) error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return } - t.Run(test.Name, func(t *testing.T) { + t.Run(test.Name(), func(t *testing.T) { err := f(t) assert.NoError(t, err) }) }, mode, - speedEnv, func(t *testing.T, expected string, actual string, prefix string) { t.Helper() assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) diff --git a/pkg/gui/input.go b/pkg/gui/input.go new file mode 100644 index 000000000..c6424c077 --- /dev/null +++ b/pkg/gui/input.go @@ -0,0 +1,93 @@ +package gui + +import ( + "time" + + "github.com/gdamore/tcell/v2" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type InputImpl struct { + g *gocui.Gui + keys config.KeybindingConfig +} + +var _ types.Input = &InputImpl{} + +func (self *InputImpl) PushKeys(keyStrs ...string) { + for _, keyStr := range keyStrs { + self.pushKey(keyStr) + } +} + +func (self *InputImpl) pushKey(keyStr string) { + key := keybindings.GetKey(keyStr) + + var r rune + var tcellKey tcell.Key + switch v := key.(type) { + case rune: + r = v + tcellKey = tcell.KeyRune + case gocui.Key: + tcellKey = tcell.Key(v) + } + + self.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( + tcell.NewEventKey(tcellKey, r, tcell.ModNone), + 0, + ) +} + +func (self *InputImpl) SwitchToStatusWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[0]) +} + +func (self *InputImpl) SwitchToFilesWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[1]) +} + +func (self *InputImpl) SwitchToBranchesWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[2]) +} + +func (self *InputImpl) SwitchToCommitsWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[3]) +} + +func (self *InputImpl) SwitchToStashWindow() { + self.pushKey(self.keys.Universal.JumpToBlock[4]) +} + +func (self *InputImpl) Type(content string) { + for _, char := range content { + self.pushKey(string(char)) + } +} + +func (self *InputImpl) Confirm() { + self.pushKey(self.keys.Universal.Confirm) +} + +func (self *InputImpl) Cancel() { + self.pushKey(self.keys.Universal.Return) +} + +func (self *InputImpl) Select() { + self.pushKey(self.keys.Universal.Select) +} + +func (self *InputImpl) NextItem() { + self.pushKey(self.keys.Universal.NextItem) +} + +func (self *InputImpl) PreviousItem() { + self.pushKey(self.keys.Universal.PrevItem) +} + +func (self *InputImpl) Wait(milliseconds int) { + time.Sleep(time.Duration(milliseconds) * time.Millisecond) +} diff --git a/pkg/gui/old_gui_test.go b/pkg/gui/old_gui_test.go new file mode 100644 index 000000000..12e33432d --- /dev/null +++ b/pkg/gui/old_gui_test.go @@ -0,0 +1,76 @@ +//go:build !windows +// +build !windows + +package gui + +import ( + "fmt" + "os" + "testing" + + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/stretchr/testify/assert" +) + +// Deprecated: this is the old way of running tests. See pkg/gui/gui_test.go for the new way. + +// This file is quite similar to integration/main.go. The main difference is that this file is +// run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides +// a convenient gui wrapper around our integration tests. The `go test` approach is better +// for CI and for running locally in the background to ensure you haven't broken +// anything while making changes. If you want to visually see what's happening when a test is run, +// you'll need to take the other approach +// +// As for this file, to run an integration test, e.g. for test 'commit', go: +// go test pkg/gui/old_gui_test.go -run /commit +// +// To update a snapshot for an integration test, pass UPDATE_SNAPSHOTS=true +// UPDATE_SNAPSHOTS=true go test pkg/gui/old_gui_test.go -run /commit +// +// integration tests are run in test/integration//actual and the final test does +// not clean up that directory so you can cd into it to see for yourself what +// happened when a test fails. +// +// To override speed, pass e.g. `SPEED=1` as an env var. Otherwise we start each test +// at a high speed and then drop down to lower speeds upon each failure until finally +// trying at the original playback speed (speed 1). A speed of 2 represents twice the +// original playback speed. Speed may be a decimal. + +func TestOld(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration tests in short mode") + } + + mode := integration.GetModeFromEnv() + speedEnv := os.Getenv("SPEED") + includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" + + parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) + parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) + testNumber := 0 + + err := integration.RunTests( + t.Logf, + runCmdHeadless, + func(test *integration.Test, f func(*testing.T) error) { + defer func() { testNumber += 1 }() + if testNumber%parallelTotal != parallelIndex { + return + } + + t.Run(test.Name, func(t *testing.T) { + err := f(t) + assert.NoError(t, err) + }) + }, + mode, + speedEnv, + func(t *testing.T, expected string, actual string, prefix string) { + t.Helper() + assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) + }, + includeSkipped, + ) + + assert.NoError(t, err) +} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go new file mode 100644 index 000000000..88e42e440 --- /dev/null +++ b/pkg/gui/test_mode.go @@ -0,0 +1,63 @@ +package gui + +import ( + "fmt" + "log" + "time" + + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +func (gui *Gui) handleTestMode() { + if integration.PlayingIntegrationTest() { + test, ok := integration.CurrentIntegrationTest() + + if !ok { + panic(fmt.Sprintf("test %s not found", integration.IntegrationTestName())) + } + + go func() { + time.Sleep(time.Millisecond * 100) + + test.Run( + &integration.ShellImpl{}, + &InputImpl{g: gui.g, keys: gui.Config.GetUserConfig().Keybinding}, + &AssertImpl{gui: gui}, + gui.c.UserConfig.Keybinding, + ) + + gui.g.Update(func(*gocui.Gui) error { + return gocui.ErrQuit + }) + + time.Sleep(time.Second * 1) + + log.Fatal("gocui should have already exited") + }() + + go utils.Safe(func() { + time.Sleep(time.Second * 40) + log.Fatal("40 seconds is up, lazygit recording took too long to complete") + }) + } + + if integration.Replaying() { + gui.g.RecordingConfig = gocui.RecordingConfig{ + Speed: integration.GetRecordingSpeed(), + Leeway: 100, + } + + var err error + gui.g.Recording, err = integration.LoadRecording() + if err != nil { + panic(err) + } + + go utils.Safe(func() { + time.Sleep(time.Second * 40) + log.Fatal("40 seconds is up, lazygit recording took too long to complete") + }) + } +} diff --git a/pkg/integration/env.go b/pkg/integration/env.go new file mode 100644 index 000000000..7cdc72267 --- /dev/null +++ b/pkg/integration/env.go @@ -0,0 +1,46 @@ +package integration + +import ( + "os" + + "github.com/jesseduffield/generics/slices" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +func Headless() bool { + return os.Getenv("HEADLESS") != "" +} + +// NEW integration test format stuff + +func IntegrationTestName() string { + return os.Getenv("LAZYGIT_TEST_NAME") +} + +func CurrentIntegrationTest() (types.Test, bool) { + if !PlayingIntegrationTest() { + return nil, false + } + + return slices.Find(Tests, func(test types.Test) bool { + return test.Name() == IntegrationTestName() + }) +} + +func PlayingIntegrationTest() bool { + return IntegrationTestName() != "" +} + +// OLD integration test format stuff + +func Replaying() bool { + return os.Getenv("REPLAY_EVENTS_FROM") != "" +} + +func RecordingEvents() bool { + return recordEventsTo() != "" +} + +func recordEventsTo() string { + return os.Getenv("RECORD_EVENTS_TO") +} diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 4172f3e49..853dae0d9 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -1,72 +1,29 @@ package integration import ( - "encoding/json" "errors" "fmt" "io/ioutil" - "log" "os" "os/exec" "path/filepath" - "strconv" - "strings" "testing" - "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" + "github.com/jesseduffield/lazygit/pkg/integration/types" ) -// This package is for running our integration test suite. See docs/Integration_Tests.md for more info +// this is the integration runner for the new and improved integration interface -type Test struct { - Name string `json:"name"` - Speed float64 `json:"speed"` - Description string `json:"description"` - ExtraCmdArgs string `json:"extraCmdArgs"` - Skip bool `json:"skip"` -} +// re-exporting this so that clients only need to import one package +var Tests = integration_tests.Tests -type Mode int - -const ( - // default: for when we're just running a test and comparing to the snapshot - TEST = iota - // for when we want to record a test and set the snapshot based on the result - RECORD - // when we just want to use the setup of the test for our own sandboxing purposes. - // This does not record the session and does not create/update snapshots - SANDBOX - // running a test but updating the snapshot - UPDATE_SNAPSHOT -) - -func GetModeFromEnv() Mode { - switch os.Getenv("MODE") { - case "record": - return RECORD - case "", "test": - return TEST - case "updateSnapshot": - return UPDATE_SNAPSHOT - case "sandbox": - return SANDBOX - default: - log.Fatalf("unknown test mode: %s, must be one of [test, record, update, sandbox]", os.Getenv("MODE")) - panic("unreachable") - } -} - -// this function is used by both `go test` and from our lazyintegration gui, but -// errors need to be handled differently in each (for example go test is always -// working with *testing.T) so we pass in any differences as args here. -func RunTests( +func RunTestsNew( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test *Test, f func(*testing.T) error), + fnWrapper func(test types.Test, f func(*testing.T) error), mode Mode, - speedEnv string, onFail func(t *testing.T, expected string, actual string, prefix string), includeSkipped bool, ) error { @@ -76,7 +33,7 @@ func RunTests( return err } - testDir := filepath.Join(rootDir, "test", "integration") + testDir := filepath.Join(rootDir, "test", "integration_new") osCommand := oscommands.NewDummyOSCommand() err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run() @@ -84,115 +41,94 @@ func RunTests( return err } - tests, err := LoadTests(testDir) - if err != nil { - return err - } - - for _, test := range tests { + for _, test := range Tests { test := test fnWrapper(test, func(t *testing.T) error { //nolint: thelper - if test.Skip && !includeSkipped { - logf("skipping test: %s", test.Name) + if test.Skip() && !includeSkipped { + logf("skipping test: %s", test.Name()) return nil } - speeds := getTestSpeeds(test.Speed, mode, speedEnv) - testPath := filepath.Join(testDir, test.Name) + testPath := filepath.Join(testDir, test.Name()) + actualDir := filepath.Join(testPath, "actual") expectedDir := filepath.Join(testPath, "expected") actualRepoDir := filepath.Join(actualDir, "repo") logf("path: %s", testPath) - for i, speed := range speeds { - if mode != SANDBOX && mode != RECORD { - logf("%s: attempting test at speed %f\n", test.Name, speed) - } + findOrCreateDir(testPath) + prepareIntegrationTestDir(actualDir) + findOrCreateDir(actualRepoDir) + err := createFixtureNew(test, actualRepoDir, rootDir) + if err != nil { + return err + } - findOrCreateDir(testPath) - prepareIntegrationTestDir(actualDir) - findOrCreateDir(actualRepoDir) - err := createFixture(testPath, actualRepoDir) + configDir := filepath.Join(testPath, "used_config") + + cmd, err := getLazygitCommandNew(test, testPath, rootDir) + if err != nil { + return err + } + + err = runCmd(cmd) + if err != nil { + return err + } + + if mode == UPDATE_SNAPSHOT { + // create/update snapshot + err = oscommands.CopyDir(actualDir, expectedDir) if err != nil { return err } - configDir := filepath.Join(testPath, "used_config") + if err := renameSpecialPaths(expectedDir); err != nil { + return err + } - cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs) + logf("%s", "updated snapshot") + } else { + if err := validateSameRepos(expectedDir, actualDir); err != nil { + return err + } + + // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDir) if err != nil { return err } - err = runCmd(cmd) - if err != nil { - return err - } + for _, f := range expectedFiles { + if !f.IsDir() { + return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") + } - if mode == UPDATE_SNAPSHOT || mode == RECORD { - // create/update snapshot - err = oscommands.CopyDir(actualDir, expectedDir) + // get corresponding file name from actual dir + actualRepoPath := filepath.Join(actualDir, f.Name()) + expectedRepoPath := filepath.Join(expectedDir, f.Name()) + + actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) if err != nil { return err } - if err := renameSpecialPaths(expectedDir); err != nil { - return err - } - - logf("%s", "updated snapshot") - } else { - if err := validateSameRepos(expectedDir, actualDir); err != nil { - return err - } - - // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir - expectedFiles, err := ioutil.ReadDir(expectedDir) - if err != nil { - return err - } - - success := true - for _, f := range expectedFiles { - if !f.IsDir() { - return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") - } - - // get corresponding file name from actual dir - actualRepoPath := filepath.Join(actualDir, f.Name()) - expectedRepoPath := filepath.Join(expectedDir, f.Name()) - - actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) + if expectedRepo != actualRepo { + // get the log file and print it + bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) if err != nil { return err } + logf("%s", string(bytes)) - if expectedRepo != actualRepo { - success = false - // if the snapshot doesn't match and we haven't tried all playback speeds different we'll retry at a slower speed - if i < len(speeds)-1 { - break - } - - // get the log file and print it - bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) - if err != nil { - return err - } - logf("%s", string(bytes)) - - onFail(t, expectedRepo, actualRepo, f.Name()) - } - } - - if success { - logf("%s: success at speed %f\n", test.Name, speed) - break + onFail(t, expectedRepo, actualRepo, f.Name()) } } } + logf("test passed: %s", test.Name()) + return nil }) } @@ -200,344 +136,35 @@ func RunTests( return nil } -// validates that the actual and expected dirs have the same repo names (doesn't actually check the contents of the repos) -func validateSameRepos(expectedDir string, actualDir string) error { - // iterate through each repo in the expected dir and compare to the corresponding repo in the actual dir - expectedFiles, err := ioutil.ReadDir(expectedDir) - if err != nil { - return err - } - - var actualFiles []os.FileInfo - actualFiles, err = ioutil.ReadDir(actualDir) - if err != nil { - return err - } - - expectedFileNames := slices.Map(expectedFiles, getFileName) - actualFileNames := slices.Map(actualFiles, getFileName) - if !slices.Equal(expectedFileNames, actualFileNames) { - return fmt.Errorf("expected and actual repo dirs do not match: expected: %s, actual: %s", expectedFileNames, actualFileNames) - } - - return nil -} - -func getFileName(f os.FileInfo) string { - return f.Name() -} - -func prepareIntegrationTestDir(actualDir string) { - // remove contents of integration test directory - dir, err := ioutil.ReadDir(actualDir) - if err != nil { - if os.IsNotExist(err) { - err = os.Mkdir(actualDir, 0o777) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } - for _, d := range dir { - os.RemoveAll(filepath.Join(actualDir, d.Name())) - } -} - -func GetRootDirectory() string { - path, err := os.Getwd() - if err != nil { +func createFixtureNew(test types.Test, actualDir string, rootDir string) error { + if err := os.Chdir(actualDir); err != nil { panic(err) } - for { - _, err := os.Stat(filepath.Join(path, ".git")) + shell := &ShellImpl{} + shell.RunCommand("git init") + shell.RunCommand(`git config user.email "CI@example.com"`) + shell.RunCommand(`git config user.name "CI"`) - if err == nil { - return path - } + test.SetupRepo(shell) - if !os.IsNotExist(err) { - panic(err) - } - - path = filepath.Dir(path) - - if path == "/" { - log.Fatal("must run in lazygit folder or child folder") - } - } -} - -func createFixture(testPath, actualDir string) error { - bashScriptPath := filepath.Join(testPath, "setup.sh") - cmd := secureexec.Command("bash", bashScriptPath, actualDir) - - if output, err := cmd.CombinedOutput(); err != nil { - return errors.New(string(output)) - } - - return nil -} - -func tempLazygitPath() string { - return filepath.Join("/tmp", "lazygit", "test_lazygit") -} - -func getTestSpeeds(testStartSpeed float64, mode Mode, speedStr string) []float64 { - if mode != TEST { - // have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot - return []float64{1.0} - } - - if speedStr != "" { - speed, err := strconv.ParseFloat(speedStr, 64) - if err != nil { - panic(err) - } - return []float64{speed} - } - - // default is 10, 5, 1 - startSpeed := 10.0 - if testStartSpeed != 0 { - startSpeed = testStartSpeed - } - speeds := []float64{startSpeed} - if startSpeed > 5 { - speeds = append(speeds, 5) - } - speeds = append(speeds, 1, 1) - - return speeds -} - -func LoadTests(testDir string) ([]*Test, error) { - paths, err := filepath.Glob(filepath.Join(testDir, "/*/test.json")) - if err != nil { - return nil, err - } - - tests := make([]*Test, len(paths)) - - for i, path := range paths { - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - test := &Test{} - - err = json.Unmarshal(data, test) - if err != nil { - return nil, err - } - - test.Name = strings.TrimPrefix(filepath.Dir(path), testDir+"/") - - tests[i] = test - } - - return tests, nil -} - -func findOrCreateDir(path string) { - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - err = os.MkdirAll(path, 0o777) - if err != nil { - panic(err) - } - } else { - panic(err) - } - } -} - -// note that we don't actually store this snapshot in the lazygit repo. -// Instead we store the whole expected git repo of our test, so that -// we can easily change what we want to compare without needing to regenerate -// snapshots for each test. -func generateSnapshot(dir string) (string, error) { - osCommand := oscommands.NewDummyOSCommand() - - _, err := os.Stat(filepath.Join(dir, ".git")) - if err != nil { - return "git directory not found", nil - } - - snapshot := "" - - cmdStrs := []string{ - `remote show -n origin`, // remote branches - // TODO: find a way to bring this back without breaking tests - // `ls-remote origin`, - `status`, // file tree - `log --pretty=%B|%an|%ae -p -1`, // log - `tag -n`, // tags - `stash list`, // stash - `submodule foreach 'git status'`, // submodule status - `submodule foreach 'git log --pretty=%B -p -1'`, // submodule log - `submodule foreach 'git tag -n'`, // submodule tags - `submodule foreach 'git stash list'`, // submodule stash - } - - for _, cmdStr := range cmdStrs { - // ignoring error for now. If there's an error it could be that there are no results - output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput() - - snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output) - } - - snapshot += "files in repo:\n" - err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - if f.IsDir() { - if f.Name() == ".git" { - return filepath.SkipDir - } - return nil - } - - bytes, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - relativePath, err := filepath.Rel(dir, path) - if err != nil { - return err - } - snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes)) - - return nil - }) - - if err != nil { - return "", err - } - - return snapshot, nil -} - -func generateSnapshots(actualDir string, expectedDir string) (string, string, error) { - actual, err := generateSnapshot(actualDir) - if err != nil { - return "", "", err - } - - // there are a couple of reasons we're not generating the snapshot in expectedDir directly: - // Firstly we don't want to have to revert our .git file back to .git_keep. - // Secondly, the act of calling git commands like 'git status' actually changes the index - // for some reason, and we don't want to leave your lazygit working tree dirty as a result. - expectedDirCopyDir := filepath.Join(filepath.Dir(expectedDir), "expected_dir_test") - err = oscommands.CopyDir(expectedDir, expectedDirCopyDir) - if err != nil { - return "", "", err - } - - defer func() { - err := os.RemoveAll(expectedDirCopyDir) - if err != nil { - panic(err) - } - }() - - if err := restoreSpecialPaths(expectedDirCopyDir); err != nil { - return "", "", err - } - - expected, err := generateSnapshot(expectedDirCopyDir) - if err != nil { - return "", "", err - } - - return actual, expected, nil -} - -func getPathsToRename(dir string, needle string, contains string) []string { - pathsToRename := []string{} - - err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - - if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) { - pathsToRename = append(pathsToRename, path) - } - - return nil - }) - if err != nil { + // changing directory back to rootDir after the setup is done + if err := os.Chdir(rootDir); err != nil { panic(err) } - return pathsToRename -} - -var specialPathMappings = []struct{ original, new, contains string }{ - // git refuses to track .git or .gitmodules in subdirectories so we need to rename them - {".git", ".git_keep", ""}, - {".gitmodules", ".gitmodules_keep", ""}, - // we also need git to ignore the contents of our test gitignore files so that - // we actually commit files that are ignored within the test. - {".gitignore", "lg_ignore_file", ""}, - // this is the .git/info/exclude file. We're being a little more specific here - // so that we don't accidentally mess with some other file named 'exclude' in the test. - {"exclude", "lg_exclude_file", ".git/info/exclude"}, -} - -func renameSpecialPaths(dir string) error { - for _, specialPath := range specialPathMappings { - for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) { - err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new)) - if err != nil { - return err - } - } - } - return nil } -func restoreSpecialPaths(dir string) error { - for _, specialPath := range specialPathMappings { - for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) { - err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original)) - if err != nil { - return err - } - } - } - - return nil -} - -func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) { +func getLazygitCommandNew(test types.Test, testPath string, rootDir string) (*exec.Cmd, error) { osCommand := oscommands.NewDummyOSCommand() - replayPath := filepath.Join(testPath, "recording.json") templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") actualRepoDir := filepath.Join(testPath, "actual", "repo") - exists, err := osCommand.FileExists(filepath.Join(testPath, "config")) - if err != nil { - return nil, err - } - - if exists { - templateConfigDir = filepath.Join(testPath, "config") - } - configDir := filepath.Join(testPath, "used_config") - err = os.RemoveAll(configDir) + err := os.RemoveAll(configDir) if err != nil { return nil, err } @@ -546,17 +173,11 @@ func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64 return nil, err } - cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, extraCmdArgs) + cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, test.ExtraCmdArgs()) cmdObj := osCommand.Cmd.New(cmdStr) - cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed)) - switch mode { - case RECORD: - cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath)) - case TEST, UPDATE_SNAPSHOT: - cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath)) - } + cmdObj.AddEnvVars(fmt.Sprintf("LAZYGIT_TEST_NAME=%s", test.Name())) return cmdObj.GetCmd(), nil } diff --git a/pkg/integration/integration_old.go b/pkg/integration/integration_old.go new file mode 100644 index 000000000..579892e3b --- /dev/null +++ b/pkg/integration/integration_old.go @@ -0,0 +1,564 @@ +package integration + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/jesseduffield/generics/slices" + "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/secureexec" +) + +// This package is for running our integration test suite. See docs/Integration_Tests.md for more info. + +// Deprecated: This file is part of the old way of doing things. See integration.go for the new way + +type Test struct { + Name string `json:"name"` + Speed float64 `json:"speed"` + Description string `json:"description"` + ExtraCmdArgs string `json:"extraCmdArgs"` + Skip bool `json:"skip"` +} + +type Mode int + +const ( + // default: for when we're just running a test and comparing to the snapshot + TEST = iota + // for when we want to record a test and set the snapshot based on the result + RECORD + // when we just want to use the setup of the test for our own sandboxing purposes. + // This does not record the session and does not create/update snapshots + SANDBOX + // running a test but updating the snapshot + UPDATE_SNAPSHOT +) + +func GetModeFromEnv() Mode { + switch os.Getenv("MODE") { + case "record": + return RECORD + case "", "test": + return TEST + case "updateSnapshot": + return UPDATE_SNAPSHOT + case "sandbox": + return SANDBOX + default: + log.Fatalf("unknown test mode: %s, must be one of [test, record, updateSnapshot, sandbox]", os.Getenv("MODE")) + panic("unreachable") + } +} + +// this function is used by both `go test` and from our lazyintegration gui, but +// errors need to be handled differently in each (for example go test is always +// working with *testing.T) so we pass in any differences as args here. +func RunTests( + logf func(format string, formatArgs ...interface{}), + runCmd func(cmd *exec.Cmd) error, + fnWrapper func(test *Test, f func(*testing.T) error), + mode Mode, + speedEnv string, + onFail func(t *testing.T, expected string, actual string, prefix string), + includeSkipped bool, +) error { + rootDir := GetRootDirectory() + err := os.Chdir(rootDir) + if err != nil { + return err + } + + testDir := filepath.Join(rootDir, "test", "integration") + + osCommand := oscommands.NewDummyOSCommand() + err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run() + if err != nil { + return err + } + + tests, err := LoadTests(testDir) + if err != nil { + return err + } + + for _, test := range tests { + test := test + + fnWrapper(test, func(t *testing.T) error { //nolint: thelper + if test.Skip && !includeSkipped { + logf("skipping test: %s", test.Name) + return nil + } + + speeds := getTestSpeeds(test.Speed, mode, speedEnv) + testPath := filepath.Join(testDir, test.Name) + actualDir := filepath.Join(testPath, "actual") + expectedDir := filepath.Join(testPath, "expected") + actualRepoDir := filepath.Join(actualDir, "repo") + logf("path: %s", testPath) + + for i, speed := range speeds { + if mode != SANDBOX && mode != RECORD { + logf("%s: attempting test at speed %f\n", test.Name, speed) + } + + findOrCreateDir(testPath) + prepareIntegrationTestDir(actualDir) + findOrCreateDir(actualRepoDir) + err := createFixture(testPath, actualRepoDir) + if err != nil { + return err + } + + configDir := filepath.Join(testPath, "used_config") + + cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs) + if err != nil { + return err + } + + err = runCmd(cmd) + if err != nil { + return err + } + + if mode == UPDATE_SNAPSHOT || mode == RECORD { + // create/update snapshot + err = oscommands.CopyDir(actualDir, expectedDir) + if err != nil { + return err + } + + if err := renameSpecialPaths(expectedDir); err != nil { + return err + } + + logf("%s", "updated snapshot") + } else { + if err := validateSameRepos(expectedDir, actualDir); err != nil { + return err + } + + // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDir) + if err != nil { + return err + } + + success := true + for _, f := range expectedFiles { + if !f.IsDir() { + return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") + } + + // get corresponding file name from actual dir + actualRepoPath := filepath.Join(actualDir, f.Name()) + expectedRepoPath := filepath.Join(expectedDir, f.Name()) + + actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) + if err != nil { + return err + } + + if expectedRepo != actualRepo { + success = false + // if the snapshot doesn't match and we haven't tried all playback speeds different we'll retry at a slower speed + if i < len(speeds)-1 { + break + } + + // get the log file and print it + bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) + if err != nil { + return err + } + logf("%s", string(bytes)) + + onFail(t, expectedRepo, actualRepo, f.Name()) + } + } + + if success { + logf("%s: success at speed %f\n", test.Name, speed) + break + } + } + } + + return nil + }) + } + + return nil +} + +// validates that the actual and expected dirs have the same repo names (doesn't actually check the contents of the repos) +func validateSameRepos(expectedDir string, actualDir string) error { + // iterate through each repo in the expected dir and compare to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDir) + if err != nil { + return err + } + + var actualFiles []os.FileInfo + actualFiles, err = ioutil.ReadDir(actualDir) + if err != nil { + return err + } + + expectedFileNames := slices.Map(expectedFiles, getFileName) + actualFileNames := slices.Map(actualFiles, getFileName) + if !slices.Equal(expectedFileNames, actualFileNames) { + return fmt.Errorf("expected and actual repo dirs do not match: expected: %s, actual: %s", expectedFileNames, actualFileNames) + } + + return nil +} + +func getFileName(f os.FileInfo) string { + return f.Name() +} + +func prepareIntegrationTestDir(actualDir string) { + // remove contents of integration test directory + dir, err := ioutil.ReadDir(actualDir) + if err != nil { + if os.IsNotExist(err) { + err = os.Mkdir(actualDir, 0o777) + if err != nil { + panic(err) + } + } else { + panic(err) + } + } + for _, d := range dir { + os.RemoveAll(filepath.Join(actualDir, d.Name())) + } +} + +func GetRootDirectory() string { + path, err := os.Getwd() + if err != nil { + panic(err) + } + + for { + _, err := os.Stat(filepath.Join(path, ".git")) + + if err == nil { + return path + } + + if !os.IsNotExist(err) { + panic(err) + } + + path = filepath.Dir(path) + + if path == "/" { + log.Fatal("must run in lazygit folder or child folder") + } + } +} + +func createFixture(testPath, actualDir string) error { + bashScriptPath := filepath.Join(testPath, "setup.sh") + cmd := secureexec.Command("bash", bashScriptPath, actualDir) + + if output, err := cmd.CombinedOutput(); err != nil { + return errors.New(string(output)) + } + + return nil +} + +func tempLazygitPath() string { + return filepath.Join("/tmp", "lazygit", "test_lazygit") +} + +func getTestSpeeds(testStartSpeed float64, mode Mode, speedStr string) []float64 { + if mode != TEST { + // have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot + return []float64{1.0} + } + + if speedStr != "" { + speed, err := strconv.ParseFloat(speedStr, 64) + if err != nil { + panic(err) + } + return []float64{speed} + } + + // default is 10, 5, 1 + startSpeed := 10.0 + if testStartSpeed != 0 { + startSpeed = testStartSpeed + } + speeds := []float64{startSpeed} + if startSpeed > 5 { + speeds = append(speeds, 5) + } + speeds = append(speeds, 1, 1) + + return speeds +} + +func LoadTests(testDir string) ([]*Test, error) { + paths, err := filepath.Glob(filepath.Join(testDir, "/*/test.json")) + if err != nil { + return nil, err + } + + tests := make([]*Test, len(paths)) + + for i, path := range paths { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + test := &Test{} + + err = json.Unmarshal(data, test) + if err != nil { + return nil, err + } + + test.Name = strings.TrimPrefix(filepath.Dir(path), testDir+"/") + + tests[i] = test + } + + return tests, nil +} + +func findOrCreateDir(path string) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(path, 0o777) + if err != nil { + panic(err) + } + } else { + panic(err) + } + } +} + +// note that we don't actually store this snapshot in the lazygit repo. +// Instead we store the whole expected git repo of our test, so that +// we can easily change what we want to compare without needing to regenerate +// snapshots for each test. +func generateSnapshot(dir string) (string, error) { + osCommand := oscommands.NewDummyOSCommand() + + _, err := os.Stat(filepath.Join(dir, ".git")) + if err != nil { + return "git directory not found", nil + } + + snapshot := "" + + cmdStrs := []string{ + `remote show -n origin`, // remote branches + // TODO: find a way to bring this back without breaking tests + // `ls-remote origin`, + `status`, // file tree + `log --pretty=%B|%an|%ae -p -1`, // log + `tag -n`, // tags + `stash list`, // stash + `submodule foreach 'git status'`, // submodule status + `submodule foreach 'git log --pretty=%B -p -1'`, // submodule log + `submodule foreach 'git tag -n'`, // submodule tags + `submodule foreach 'git stash list'`, // submodule stash + } + + for _, cmdStr := range cmdStrs { + // ignoring error for now. If there's an error it could be that there are no results + output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput() + + snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output) + } + + snapshot += "files in repo:\n" + err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.IsDir() { + if f.Name() == ".git" { + return filepath.SkipDir + } + return nil + } + + bytes, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + relativePath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes)) + + return nil + }) + + if err != nil { + return "", err + } + + return snapshot, nil +} + +func generateSnapshots(actualDir string, expectedDir string) (string, string, error) { + actual, err := generateSnapshot(actualDir) + if err != nil { + return "", "", err + } + + // there are a couple of reasons we're not generating the snapshot in expectedDir directly: + // Firstly we don't want to have to revert our .git file back to .git_keep. + // Secondly, the act of calling git commands like 'git status' actually changes the index + // for some reason, and we don't want to leave your lazygit working tree dirty as a result. + expectedDirCopyDir := filepath.Join(filepath.Dir(expectedDir), "expected_dir_test") + err = oscommands.CopyDir(expectedDir, expectedDirCopyDir) + if err != nil { + return "", "", err + } + + defer func() { + err := os.RemoveAll(expectedDirCopyDir) + if err != nil { + panic(err) + } + }() + + if err := restoreSpecialPaths(expectedDirCopyDir); err != nil { + return "", "", err + } + + expected, err := generateSnapshot(expectedDirCopyDir) + if err != nil { + return "", "", err + } + + return actual, expected, nil +} + +func getPathsToRename(dir string, needle string, contains string) []string { + pathsToRename := []string{} + + err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) { + pathsToRename = append(pathsToRename, path) + } + + return nil + }) + if err != nil { + panic(err) + } + + return pathsToRename +} + +var specialPathMappings = []struct{ original, new, contains string }{ + // git refuses to track .git or .gitmodules in subdirectories so we need to rename them + {".git", ".git_keep", ""}, + {".gitmodules", ".gitmodules_keep", ""}, + // we also need git to ignore the contents of our test gitignore files so that + // we actually commit files that are ignored within the test. + {".gitignore", "lg_ignore_file", ""}, + // this is the .git/info/exclude file. We're being a little more specific here + // so that we don't accidentally mess with some other file named 'exclude' in the test. + {"exclude", "lg_exclude_file", ".git/info/exclude"}, +} + +func renameSpecialPaths(dir string) error { + for _, specialPath := range specialPathMappings { + for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) { + err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new)) + if err != nil { + return err + } + } + } + + return nil +} + +func restoreSpecialPaths(dir string) error { + for _, specialPath := range specialPathMappings { + for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) { + err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original)) + if err != nil { + return err + } + } + } + + return nil +} + +func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) { + osCommand := oscommands.NewDummyOSCommand() + + replayPath := filepath.Join(testPath, "recording.json") + templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") + actualRepoDir := filepath.Join(testPath, "actual", "repo") + + exists, err := osCommand.FileExists(filepath.Join(testPath, "config")) + if err != nil { + return nil, err + } + + if exists { + templateConfigDir = filepath.Join(testPath, "config") + } + + configDir := filepath.Join(testPath, "used_config") + + err = os.RemoveAll(configDir) + if err != nil { + return nil, err + } + err = oscommands.CopyDir(templateConfigDir, configDir) + if err != nil { + return nil, err + } + + cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, extraCmdArgs) + + cmdObj := osCommand.Cmd.New(cmdStr) + cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed)) + + switch mode { + case RECORD: + cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath)) + case TEST, UPDATE_SNAPSHOT: + cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath)) + } + + return cmdObj.GetCmd(), nil +} diff --git a/pkg/integration/integration_tests/branch/suggestions.go b/pkg/integration/integration_tests/branch/suggestions.go new file mode 100644 index 000000000..ff37ec9be --- /dev/null +++ b/pkg/integration/integration_tests/branch/suggestions.go @@ -0,0 +1,40 @@ +package branch + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +var Suggestions = types.NewTest(types.NewTestArgs{ + Description: "Checking out a branch with name suggestions", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell types.Shell) { + shell. + EmptyCommit("my commit message"). + NewBranch("new-branch"). + NewBranch("new-branch-2"). + NewBranch("new-branch-3"). + NewBranch("branch-to-checkout"). + NewBranch("other-new-branch-2"). + NewBranch("other-new-branch-3") + }, + Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + input.SwitchToBranchesWindow() + + input.PushKeys(keys.Branches.CheckoutBranchByName) + assert.CurrentViewName("confirmation") + + input.Type("branch-to") + + input.PushKeys(keys.Universal.TogglePanel) + assert.CurrentViewName("suggestions") + + // we expect the first suggestion to be the branch we want because it most + // closely matches what we typed in + input.Confirm() + + assert.CurrentBranchName("branch-to-checkout") + }, +}) diff --git a/pkg/integration/integration_tests/commit/commit.go b/pkg/integration/integration_tests/commit/commit.go new file mode 100644 index 000000000..777653bc0 --- /dev/null +++ b/pkg/integration/integration_tests/commit/commit.go @@ -0,0 +1,32 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +var Commit = types.NewTest(types.NewTestArgs{ + Description: "Staging a couple files and committing", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell types.Shell) { + shell.CreateFile("myfile", "myfile content") + shell.CreateFile("myfile2", "myfile2 content") + }, + Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + assert.CommitCount(0) + + input.Select() + input.NextItem() + input.Select() + input.PushKeys(keys.Files.CommitChanges) + + commitMessage := "my commit message" + input.Type(commitMessage) + input.Confirm() + + assert.CommitCount(1) + assert.HeadCommitMessage(commitMessage) + }, +}) diff --git a/pkg/integration/integration_tests/commit/new_branch.go b/pkg/integration/integration_tests/commit/new_branch.go new file mode 100644 index 000000000..d70515280 --- /dev/null +++ b/pkg/integration/integration_tests/commit/new_branch.go @@ -0,0 +1,38 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +var NewBranch = types.NewTest(types.NewTestArgs{ + Description: "Creating a new branch from a commit", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell types.Shell) { + shell. + EmptyCommit("commit 1"). + EmptyCommit("commit 2"). + EmptyCommit("commit 3") + }, + Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + assert.CommitCount(3) + + input.SwitchToCommitsWindow() + assert.CurrentViewName("commits") + input.NextItem() + + input.PushKeys(keys.Universal.New) + + assert.CurrentViewName("confirmation") + + branchName := "my-branch-name" + input.Type(branchName) + input.Confirm() + + assert.CommitCount(2) + assert.HeadCommitMessage("commit 2") + assert.CurrentBranchName(branchName) + }, +}) diff --git a/pkg/integration/integration_tests/tests.go b/pkg/integration/integration_tests/tests.go new file mode 100644 index 000000000..1470ef178 --- /dev/null +++ b/pkg/integration/integration_tests/tests.go @@ -0,0 +1,16 @@ +package integration_tests + +import ( + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/branch" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/commit" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +// Here is where we lists the actual tests that will run. When you create a new test, +// be sure to add it to this list. + +var Tests = []types.Test{ + commit.Commit, + commit.NewBranch, + branch.Suggestions, +} diff --git a/pkg/gui/recording.go b/pkg/integration/recording.go similarity index 62% rename from pkg/gui/recording.go rename to pkg/integration/recording.go index 9edd50f08..3fa72aac1 100644 --- a/pkg/gui/recording.go +++ b/pkg/integration/recording.go @@ -1,4 +1,4 @@ -package gui +package integration import ( "encoding/json" @@ -10,23 +10,10 @@ import ( "github.com/jesseduffield/gocui" ) -func recordingEvents() bool { - return recordEventsTo() != "" -} +// this all relates to the old way of doing integration tests where you record yourself +// and then replay the events. -func recordEventsTo() string { - return os.Getenv("RECORD_EVENTS_TO") -} - -func replaying() bool { - return os.Getenv("REPLAY_EVENTS_FROM") != "" -} - -func headless() bool { - return os.Getenv("HEADLESS") != "" -} - -func getRecordingSpeed() float64 { +func GetRecordingSpeed() float64 { // humans are slow so this speeds things up. speed := 1.0 envReplaySpeed := os.Getenv("SPEED") @@ -40,7 +27,7 @@ func getRecordingSpeed() float64 { return speed } -func (gui *Gui) loadRecording() (*gocui.Recording, error) { +func LoadRecording() (*gocui.Recording, error) { path := os.Getenv("REPLAY_EVENTS_FROM") data, err := ioutil.ReadFile(path) @@ -58,8 +45,8 @@ func (gui *Gui) loadRecording() (*gocui.Recording, error) { return recording, nil } -func (gui *Gui) saveRecording(recording *gocui.Recording) error { - if !recordingEvents() { +func SaveRecording(recording *gocui.Recording) error { + if !RecordingEvents() { return nil } diff --git a/pkg/integration/shell.go b/pkg/integration/shell.go new file mode 100644 index 000000000..69bccb66c --- /dev/null +++ b/pkg/integration/shell.go @@ -0,0 +1,53 @@ +package integration + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/mgutz/str" +) + +type ShellImpl struct{} + +var _ types.Shell = &ShellImpl{} + +func (s *ShellImpl) RunCommand(cmdStr string) types.Shell { + args := str.ToArgv(cmdStr) + cmd := secureexec.Command(args[0], args[1:]...) + cmd.Env = os.Environ() + + output, err := cmd.CombinedOutput() + if err != nil { + panic(fmt.Sprintf("error running command: %s\n%s", cmdStr, string(output))) + } + + return s +} + +func (s *ShellImpl) CreateFile(path string, content string) types.Shell { + err := ioutil.WriteFile(path, []byte(content), 0o644) + if err != nil { + panic(fmt.Sprintf("error creating file: %s\n%s", path, err)) + } + + return s +} + +func (s *ShellImpl) NewBranch(name string) types.Shell { + return s.RunCommand("git checkout -b " + name) +} + +func (s *ShellImpl) GitAddAll() types.Shell { + return s.RunCommand("git add -A") +} + +func (s *ShellImpl) Commit(message string) types.Shell { + return s.RunCommand(fmt.Sprintf("git commit -m \"%s\"", message)) +} + +func (s *ShellImpl) EmptyCommit(message string) types.Shell { + return s.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message)) +} diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go new file mode 100644 index 000000000..29b303f35 --- /dev/null +++ b/pkg/integration/types/types.go @@ -0,0 +1,152 @@ +package types + +import ( + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +type Test interface { + Name() string + Description() string + // this is called before lazygit is run, for the sake of preparing the repo + SetupRepo(Shell) + // this gives you the default config and lets you set whatever values on it you like, + // so that they appear when lazygit runs + SetupConfig(config *config.AppConfig) + // this is called upon lazygit starting + Run(Shell, Input, Assert, config.KeybindingConfig) + // e.g. '-debug' + ExtraCmdArgs() string + // for tests that are flakey and when we don't have time to fix them + Skip() bool +} + +// this is for running shell commands, mostly for the sake of setting up the repo +// but you can also run the commands from within lazygit to emulate things happening +// in the background. +// Implementation is at pkg/integration/shell.go +type Shell interface { + RunCommand(command string) Shell + CreateFile(name string, content string) Shell + NewBranch(branchName string) Shell + GitAddAll() Shell + Commit(message string) Shell + EmptyCommit(message string) Shell +} + +// through this interface our test interacts with the lazygit gui +// Implementation is at pkg/gui/input.go +type Input interface { + // key is something like 'w' or ''. It's best not to pass a direct value, + // but instead to go through the default user config to get a more meaningful key name + PushKeys(keys ...string) + // for typing into a popup prompt + Type(content string) + // for when you want to allow lazygit to process something before continuing + Wait(milliseconds int) + // going straight to a particular side window + SwitchToStatusWindow() + SwitchToFilesWindow() + SwitchToBranchesWindow() + SwitchToCommitsWindow() + SwitchToStashWindow() + // i.e. pressing enter + Confirm() + // i.e. pressing escape + Cancel() + // i.e. pressing space + Select() + // i.e. pressing down arrow + NextItem() + // i.e. pressing up arrow + PreviousItem() +} + +// through this interface we assert on the state of the lazygit gui +type Assert interface { + WorkingTreeFileCount(int) + CommitCount(int) + HeadCommitMessage(string) + CurrentViewName(expectedViewName string) + CurrentBranchName(expectedBranchName string) +} + +type TestImpl struct { + name string + description string + extraCmdArgs string + skip bool + setupRepo func(shell Shell) + setupConfig func(config *config.AppConfig) + run func( + shell Shell, + input Input, + assert Assert, + keys config.KeybindingConfig, + ) +} + +type NewTestArgs struct { + Description string + SetupRepo func(shell Shell) + SetupConfig func(config *config.AppConfig) + Run func(shell Shell, input Input, assert Assert, keys config.KeybindingConfig) + ExtraCmdArgs string + Skip bool +} + +func NewTest(args NewTestArgs) *TestImpl { + return &TestImpl{ + name: testNameFromFilePath(), + description: args.Description, + extraCmdArgs: args.ExtraCmdArgs, + skip: args.Skip, + setupRepo: args.SetupRepo, + setupConfig: args.SetupConfig, + run: args.Run, + } +} + +var _ Test = (*TestImpl)(nil) + +func (self *TestImpl) Name() string { + return self.name +} + +func (self *TestImpl) Description() string { + return self.description +} + +func (self *TestImpl) ExtraCmdArgs() string { + return self.extraCmdArgs +} + +func (self *TestImpl) Skip() bool { + return self.skip +} + +func (self *TestImpl) SetupConfig(config *config.AppConfig) { + self.setupConfig(config) +} + +func (self *TestImpl) SetupRepo(shell Shell) { + self.setupRepo(shell) +} + +func (self *TestImpl) Run( + shell Shell, + input Input, + assert Assert, + keys config.KeybindingConfig, +) { + self.run(shell, input, assert, keys) +} + +func testNameFromFilePath() string { + path := utils.FilePath(3) + name := strings.Split(path, "integration/integration_tests/")[1] + + return name[:len(name)-len(".go")] +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3f882c2b5..2f33862e8 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -128,3 +128,10 @@ func StackTrace() string { n := runtime.Stack(buf, false) return fmt.Sprintf("%s\n", buf[:n]) } + +// returns the path of the file that calls the function. +// 'skip' is the number of stack frames to skip. +func FilePath(skip int) string { + _, path, _, _ := runtime.Caller(skip) + return path +} diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration/branchSuggestions/expected/repo/.git_keep/COMMIT_EDITMSG deleted file mode 100644 index dc3ab4abe..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -file0 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/HEAD b/test/integration/branchSuggestions/expected/repo/.git_keep/HEAD deleted file mode 100644 index e2b7d4d2e..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/new-branch-3 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/index b/test/integration/branchSuggestions/expected/repo/.git_keep/index deleted file mode 100644 index fbde0e92a42dd769dac9bb6deac5f011f651c65f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137 zcmZ?q402{*U|<4b#)Rx6TXnNpD`7Mv0|N`2?j<({hQ=j8>90UFB0$V$A!2o9!9T5S z?}87#?_7HG(cY=1w-{K{GILT5fYJ;MAwjOLKuVIqP{Dvp&MtlD%-f=6YvmqvfBB$& eU-QWnr&VRm_b!PpR1;z5%bv0;j_dqpy#N67tu7M) diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/HEAD b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/HEAD deleted file mode 100644 index 4deacbbf3..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/HEAD +++ /dev/null @@ -1,8 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 commit (initial): file0 -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from master to new-branch -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from new-branch to new-branch-2 -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from new-branch-2 to new-branch-3 -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from new-branch-3 to old-branch -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from old-branch to old-branch-2 -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 checkout: moving from old-branch-2 to old-branch-3 -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675450 +1000 checkout: moving from old-branch-3 to new-branch-3 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/master b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/master deleted file mode 100644 index 8c6dabf38..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 commit (initial): file0 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-2 b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-2 deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-2 +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-3 b/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-3 deleted file mode 100644 index 530a272f0..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/logs/refs/heads/old-branch-3 +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 CI 1617675445 +1000 branch: Created from HEAD diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 b/test/integration/branchSuggestions/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 deleted file mode 100644 index 79fcadf67fdecb0f0cffca7ff27b2ae5c031e4d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50 zcmV-20L}k+0V^p=O;s>9W-v4`Ff%bxNXyJgHDIt1vAVM0pVqc_!H3>=F1`6^?^M%U I033l4YT~68jQ{`u diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da b/test/integration/branchSuggestions/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da deleted file mode 100644 index 06c9cb73d7a8ed6841ce407bd6bb15235c8fa15c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb|y2)08mZ`J^%m! diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/objects/75/e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 b/test/integration/branchSuggestions/expected/repo/.git_keep/objects/75/e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 deleted file mode 100644 index 16270b9a6..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/objects/75/e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 +++ /dev/null @@ -1,2 +0,0 @@ -xÍA -Â0Fa×9Å왉“Ä€ˆÐU‘4°Ð)<¾=‚ÛÇoé­­ƒDõ4v€WøcŒ%‹ >ø–«V¶¹T¡QÕšô¯¾Ó4Ó}šŸø¦öÞpYz{x >8UGgafsÔc2ð'7uÝÀæÜï+ö \ No newline at end of file diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/master b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/master deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-2 b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-2 deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-2 +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-3 b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-3 deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/new-branch-3 +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-2 b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-2 deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-2 +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-3 b/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-3 deleted file mode 100644 index ae478b1c7..000000000 --- a/test/integration/branchSuggestions/expected/repo/.git_keep/refs/heads/old-branch-3 +++ /dev/null @@ -1 +0,0 @@ -75e9e90a1d58c37d97d46a543dfbfd0f33fc52d8 diff --git a/test/integration/branchSuggestions/expected/repo/file0 b/test/integration/branchSuggestions/expected/repo/file0 deleted file mode 100644 index 38143ad4a..000000000 --- a/test/integration/branchSuggestions/expected/repo/file0 +++ /dev/null @@ -1 +0,0 @@ -test0 diff --git a/test/integration/branchSuggestions/recording.json b/test/integration/branchSuggestions/recording.json deleted file mode 100644 index 207dbcb3f..000000000 --- a/test/integration/branchSuggestions/recording.json +++ /dev/null @@ -1 +0,0 @@ -{"KeyEvents":[{"Timestamp":639,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1752,"Mod":0,"Key":256,"Ch":99},{"Timestamp":2183,"Mod":0,"Key":256,"Ch":110},{"Timestamp":2271,"Mod":0,"Key":256,"Ch":101},{"Timestamp":2327,"Mod":0,"Key":256,"Ch":119},{"Timestamp":2599,"Mod":0,"Key":256,"Ch":45},{"Timestamp":3583,"Mod":0,"Key":9,"Ch":9},{"Timestamp":3880,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4175,"Mod":0,"Key":13,"Ch":13},{"Timestamp":4815,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]} \ No newline at end of file diff --git a/test/integration/branchSuggestions/setup.sh b/test/integration/branchSuggestions/setup.sh deleted file mode 100644 index d67fa9291..000000000 --- a/test/integration/branchSuggestions/setup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -set -e - -cd $1 - -git init - -git config user.email "CI@example.com" -git config user.name "CI" - -echo test0 > file0 -git add . -git commit -am file0 - -git checkout -b new-branch -git checkout -b new-branch-2 -git checkout -b new-branch-3 -git checkout -b old-branch -git checkout -b old-branch-2 -git checkout -b old-branch-3 diff --git a/test/integration/branchSuggestions/test.json b/test/integration/branchSuggestions/test.json deleted file mode 100644 index fafad1962..000000000 --- a/test/integration/branchSuggestions/test.json +++ /dev/null @@ -1 +0,0 @@ -{ "description": "Checking out a branch with name suggestions", "speed": 100 } diff --git a/test/integration/commit/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration/commit/expected/repo/.git_keep/COMMIT_EDITMSG deleted file mode 100644 index 01f9a2aac..000000000 --- a/test/integration/commit/expected/repo/.git_keep/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -commit diff --git a/test/integration/commit/expected/repo/.git_keep/index b/test/integration/commit/expected/repo/.git_keep/index deleted file mode 100644 index e0cf626903ce39ebbbe5599b30490d4a4773273b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 425 zcmZ?q402{*U|<4b*2EjEX54GETVOOJP>k^#Q!N8S;}Ql2#;-s%B0$WxbkF0zs;M`= zM1)51;l?;9i64O*|xZ`Zy5 dXo>N@)8RTS%j(-r0{u^2e59|& 1617671339 +1000 commit (initial): myfile1 -3df3d8761bc0f0828596b11845aeac175b7b7393 a7d53cc21fd53100f955377be379423b0e386274 CI 1617671339 +1000 commit: myfile2 -a7d53cc21fd53100f955377be379423b0e386274 4ba4f1ed711a9081fab21bc222469aa5176a01f8 CI 1617671339 +1000 commit: myfile3 -4ba4f1ed711a9081fab21bc222469aa5176a01f8 1440bc6cc888a09dca2329d1060eec6de78d9d21 CI 1617671339 +1000 commit: myfile4 -1440bc6cc888a09dca2329d1060eec6de78d9d21 e7560e2cd4783a261ad32496cefed2d9f69a46e7 CI 1617671342 +1000 commit: commit diff --git a/test/integration/commit/expected/repo/.git_keep/logs/refs/heads/master b/test/integration/commit/expected/repo/.git_keep/logs/refs/heads/master deleted file mode 100644 index 66475b1c1..000000000 --- a/test/integration/commit/expected/repo/.git_keep/logs/refs/heads/master +++ /dev/null @@ -1,5 +0,0 @@ -0000000000000000000000000000000000000000 3df3d8761bc0f0828596b11845aeac175b7b7393 CI 1617671339 +1000 commit (initial): myfile1 -3df3d8761bc0f0828596b11845aeac175b7b7393 a7d53cc21fd53100f955377be379423b0e386274 CI 1617671339 +1000 commit: myfile2 -a7d53cc21fd53100f955377be379423b0e386274 4ba4f1ed711a9081fab21bc222469aa5176a01f8 CI 1617671339 +1000 commit: myfile3 -4ba4f1ed711a9081fab21bc222469aa5176a01f8 1440bc6cc888a09dca2329d1060eec6de78d9d21 CI 1617671339 +1000 commit: myfile4 -1440bc6cc888a09dca2329d1060eec6de78d9d21 e7560e2cd4783a261ad32496cefed2d9f69a46e7 CI 1617671342 +1000 commit: commit diff --git a/test/integration/commit/expected/repo/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 b/test/integration/commit/expected/repo/.git_keep/objects/0e/6cf0a6b79e8d44e186d812a1f74b43d64fac52 deleted file mode 100644 index 7f2ebf4eeb6ad6875bcc2a2b91ca3345ee06b45e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52 zcmboanoV|nbxTF4qCO&`WJp-0I4qNMjyQ`1 zj4XgP8F}~Ty4zv2*I~TtN7}aNWm?&AoA#~qqGW!}&p54z C&qbmD diff --git a/test/integration/commit/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 b/test/integration/commit/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 deleted file mode 100644 index f74bf2335bbc5999ad0faff94fb04165d8ab5c7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb~ZE#08nZNMgRZ+ diff --git a/test/integration/commit/expected/repo/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce b/test/integration/commit/expected/repo/.git_keep/objects/2b/173c861df433fa43ffad13f80c8b312c5c8bce deleted file mode 100644 index 0a734f98100d24e67455a3cfa8497adaccc7a422..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103 zcmV-t0GR)H0V^p=O;s>7Fl8__FfcPQQOK=K%gjkNWLUcA@n6-{8($(qqj>V2E(CbB zbYDeLV#FZ9^TVh?Y2Ue*s}_Y<^|!pzo^PR!qQsctem1Z6nX+eZ_)jSuQWx;@*VuJL J8UTCqE3ZN5G4lWb diff --git a/test/integration/commit/expected/repo/.git_keep/objects/2f/6174050380438f14b16658a356e762435ca591 b/test/integration/commit/expected/repo/.git_keep/objects/2f/6174050380438f14b16658a356e762435ca591 deleted file mode 100644 index 31ae3f5ba89b96ad2e268134913bd913a0bc46d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmV-`0Du2@0V^p=O;s>7F<>w>FfcPQQOK=K%gjkNWLUcA@n6-{8($(qqj>V2E(CbB zbYDeLV#FZ9^TVh?Y2Ue*s}_Y<^|!pzo^PR!qQsctem1Z6nX+eZ_)jSuQWx;@*VuJL i8byf-!zGiW55oT$9V>g4{^GR7m!#NRuR{Q5NjxpS$UUzB diff --git a/test/integration/commit/expected/repo/.git_keep/objects/30/a1ca3481fdec3245b02aeacfb72ddfe2a433be b/test/integration/commit/expected/repo/.git_keep/objects/30/a1ca3481fdec3245b02aeacfb72ddfe2a433be deleted file mode 100644 index aca754d63288ea16d4cd69754eac3b0cba133abe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmV;L0A>Gp0V^p=O;s>7H)Sw1FfcPQQOK=K%gjkNWLUcA@n6-{8($(qqj>V2E(CbB zbYDeLV#FZ9^TVh?Y2Ue*s}_Y<^|!pzo^PR!qQsctem1Z6nX+eZ_)jSuQWx;@*VuJL z8byf-!zGiW55oT$9V>g4{^GR7m!#NRuR~Cjm@@dA@yKkU4uUZ?d5=Uzkdm`l zj9?6woXYS1*bY6-`Z~>b`Q)}g<-%LLX$U}p05R$FxG-w0BdW`vx@zCLJ_Xee)h{{> DsryJu diff --git a/test/integration/commit/expected/repo/.git_keep/objects/4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f b/test/integration/commit/expected/repo/.git_keep/objects/4f/346f1ad5ba2917da2109e2eaa2f2dfbb86f10f deleted file mode 100644 index 953241815cfa19b4d357807bedcbb2277b2e3ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb{;fo08o?%QUCw| diff --git a/test/integration/commit/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 b/test/integration/commit/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 deleted file mode 100644 index 285df3e5fbab12262e28d85e78af8a31cd0024c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb`~^A08nuUMF0Q* diff --git a/test/integration/commit/expected/repo/.git_keep/objects/a7/341a59f0ddeef969e69fb6368266d22b0f2416 b/test/integration/commit/expected/repo/.git_keep/objects/a7/341a59f0ddeef969e69fb6368266d22b0f2416 deleted file mode 100644 index 96d2e71a6af75cdd27ac5d9628a27faecc40fb66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77 zcmV-T0J8sh0V^p=O;s>AU@$Z=Ff%bx$gNDv%tB=N-?^8o7KK;!x4hDxZ=ntVWIZ01*pecg diff --git a/test/integration/commit/expected/repo/.git_keep/objects/a7/d53cc21fd53100f955377be379423b0e386274 b/test/integration/commit/expected/repo/.git_keep/objects/a7/d53cc21fd53100f955377be379423b0e386274 deleted file mode 100644 index 77d08ca033ce47579d83912b9c8c07a2749684bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmV;H0BQet0gcX03c@fDKw;N8MfQSZGRY(ZB0^U^Mt&w(Ft(HkdV6~Ww~ue}%G&!f zG_X63T|}pV4wC0)Y87crEW|AnQnW=$SG=bUsNc z5PS|fAfrm}@mO~~&H6gcclo5gJ>^o?e$zk@C;%esGZ}lpN08nuUO8@`> diff --git a/test/integration/commit/expected/repo/.git_keep/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b b/test/integration/commit/expected/repo/.git_keep/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b deleted file mode 100644 index 9b771fc2f6f41f91b00976b4ff3f8f9935f7931e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb>`CU&08otwO#lD@ diff --git a/test/integration/commit/expected/repo/.git_keep/objects/e7/560e2cd4783a261ad32496cefed2d9f69a46e7 b/test/integration/commit/expected/repo/.git_keep/objects/e7/560e2cd4783a261ad32496cefed2d9f69a46e7 deleted file mode 100644 index 9eb3492d1ef93493ac40d908795b4f14bf9b57b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 145 zcmV;C0B-+y0gaA93c@fD06pgwxeKzpiAf3~LQj3hW_OE%v86=t`S#!gybd!kvb6T- z9*1z~o0$*5C>&zJSxo}Pa|L2!nQJE1*@$BZ)!fRPz4kChsFFlVDGY-OQ=mZ+5|~L+ z%S8tzbo}o3W%J|YFXMEx54ZJcCt2FnW5S#=hDiR10N{4IPj&lK|M~6 myfile1 -git add . -git commit -am "myfile1" -echo test2 > myfile2 -git add . -git commit -am "myfile2" -echo test3 > myfile3 -git add . -git commit -am "myfile3" -echo test4 > myfile4 -git add . -git commit -am "myfile4" -echo test5 > myfile5 diff --git a/test/integration/commit/test.json b/test/integration/commit/test.json deleted file mode 100644 index 60bfe9319..000000000 --- a/test/integration/commit/test.json +++ /dev/null @@ -1 +0,0 @@ -{ "description": "stage a file and commit the change", "speed": 15 } diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration/commitsNewBranch/expected/repo/.git_keep/COMMIT_EDITMSG deleted file mode 100644 index 6c493ff74..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/COMMIT_EDITMSG +++ /dev/null @@ -1 +0,0 @@ -file2 diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/HEAD b/test/integration/commitsNewBranch/expected/repo/.git_keep/HEAD deleted file mode 100644 index 78bc9f37b..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/lol diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/index b/test/integration/commitsNewBranch/expected/repo/.git_keep/index deleted file mode 100644 index 577a68b72172fc15a3f87e26b0a1faa60bb1bc50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209 zcmZ?q402{*U|<5_gzWu4Sc2Y#!DvPX1{OBm4Ko-R8kaCIFn$H95dmU03lXa;3;tSAy~V(qmYI`k0F-860O@C~UbPHHL(N%_ZqCv@kN>Ks-uMy`8pV_E zbRod2rTZ$x97C` 1617674232 +1000 commit (initial): file0 -9901fd9b7766be600bed07f55f1794a759527a98 0029f9bf66e346d47ede6a501abb5b82bee60096 CI 1617674232 +1000 commit: file1 -0029f9bf66e346d47ede6a501abb5b82bee60096 e1cb250774fb8606d33062518d0ae03831130249 CI 1617674232 +1000 commit: file2 -e1cb250774fb8606d33062518d0ae03831130249 0029f9bf66e346d47ede6a501abb5b82bee60096 CI 1617674249 +1000 checkout: moving from master to lol diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/lol b/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/lol deleted file mode 100644 index 1202f15d1..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/lol +++ /dev/null @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 0029f9bf66e346d47ede6a501abb5b82bee60096 CI 1617674249 +1000 branch: Created from 0029f9bf66e346d47ede6a501abb5b82bee60096 diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/master b/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/master deleted file mode 100644 index 5c02b3b2c..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/logs/refs/heads/master +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 9901fd9b7766be600bed07f55f1794a759527a98 CI 1617674232 +1000 commit (initial): file0 -9901fd9b7766be600bed07f55f1794a759527a98 0029f9bf66e346d47ede6a501abb5b82bee60096 CI 1617674232 +1000 commit: file1 -0029f9bf66e346d47ede6a501abb5b82bee60096 e1cb250774fb8606d33062518d0ae03831130249 CI 1617674232 +1000 commit: file2 diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/00/29f9bf66e346d47ede6a501abb5b82bee60096 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/00/29f9bf66e346d47ede6a501abb5b82bee60096 deleted file mode 100644 index e5731eb1f4d78631bc2c56c8724942c5ffe4bec9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmV;F0Biqv0gaAL3c@fDMqTF=*$XoHn{hxy=&Hv^CKD`JTS^3vZ;s&hKJM1*u+cZD)o7erN7unXkb?68w`rz%^0e~6nIMww}+_dGT&inv)(>XxF C-$@1l diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/18/0cf8328022becee9aaa2577a8f84ea2b9f3827 deleted file mode 100644 index f74bf2335bbc5999ad0faff94fb04165d8ab5c7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb~ZE#08nZNMgRZ+ diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/1e/3e67b999db1576ad1ee08bf4f02bdf29e49442 deleted file mode 100644 index 79fcadf67fdecb0f0cffca7ff27b2ae5c031e4d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50 zcmV-20L}k+0V^p=O;s>9W-v4`Ff%bxNXyJgHDIt1vAVM0pVqc_!H3>=F1`6^?^M%U I033l4YT~68jQ{`u diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/38/143ad4a0fe2ab6ee53c2ef89a5d9e2bd9535da deleted file mode 100644 index 06c9cb73d7a8ed6841ce407bd6bb15235c8fa15c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb|y2)08mZ`J^%m! diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/99/01fd9b7766be600bed07f55f1794a759527a98 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/99/01fd9b7766be600bed07f55f1794a759527a98 deleted file mode 100644 index cd2e8264ccaebaa8a4b849490b323543bac4e0f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 zcmV-+0Ez#20gcT;3c@fDMq$@E#q0%{Nyo&1h|pD!ku-m>&=4sRdi?eXZXX}Kxz)OK z64guJ01^yvK7|l+lDl^?3&4+5w6ION*Z>Wx*2G``w2j7zp5ye!9_x3-ZEp2N;>0_z YcCd66W;SO%CmnxsQ=F1`6^?^M%U zND2%YmhO4{S2gv2WLwJR>kD diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/a5/bce3fd2565d8f458555a0c6f42d0504a848bd5 deleted file mode 100644 index 285df3e5fbab12262e28d85e78af8a31cd0024c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21 ccmb`~^A08nuUMF0Q* diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/d0/76cc9cc09acaa2d36fbc7a95fd3e2306494641 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/d0/76cc9cc09acaa2d36fbc7a95fd3e2306494641 deleted file mode 100644 index 2e9066287..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/d0/76cc9cc09acaa2d36fbc7a95fd3e2306494641 +++ /dev/null @@ -1,2 +0,0 @@ -x+)JMU03c040031QHËÌI5`°±º²àŸÖ¶wÁ‡Þw.½ùhïTÓ[H - –îyüW5õÆ—ˆÐ(ž|§ ^-ÝW(x9 \ No newline at end of file diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/e1/cb250774fb8606d33062518d0ae03831130249 b/test/integration/commitsNewBranch/expected/repo/.git_keep/objects/e1/cb250774fb8606d33062518d0ae03831130249 deleted file mode 100644 index fc22897ccdbcdc9c2febaa311381dd9e2b929736..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmV;F0Biqv0gaA93c@fDMP26<*$Xn6wv!2n2wn9UlVpO0#+DMnCdX;-;*dqWJ;dIyxkz CNkm2f diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/lol b/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/lol deleted file mode 100644 index e92394760..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/lol +++ /dev/null @@ -1 +0,0 @@ -0029f9bf66e346d47ede6a501abb5b82bee60096 diff --git a/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/master b/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/master deleted file mode 100644 index d5689ed85..000000000 --- a/test/integration/commitsNewBranch/expected/repo/.git_keep/refs/heads/master +++ /dev/null @@ -1 +0,0 @@ -e1cb250774fb8606d33062518d0ae03831130249 diff --git a/test/integration/commitsNewBranch/expected/repo/file0 b/test/integration/commitsNewBranch/expected/repo/file0 deleted file mode 100644 index 38143ad4a..000000000 --- a/test/integration/commitsNewBranch/expected/repo/file0 +++ /dev/null @@ -1 +0,0 @@ -test0 diff --git a/test/integration/commitsNewBranch/expected/repo/file1 b/test/integration/commitsNewBranch/expected/repo/file1 deleted file mode 100644 index a5bce3fd2..000000000 --- a/test/integration/commitsNewBranch/expected/repo/file1 +++ /dev/null @@ -1 +0,0 @@ -test1 diff --git a/test/integration/commitsNewBranch/recording.json b/test/integration/commitsNewBranch/recording.json deleted file mode 100644 index ca4d07a4a..000000000 --- a/test/integration/commitsNewBranch/recording.json +++ /dev/null @@ -1 +0,0 @@ -{"KeyEvents":[{"Timestamp":972,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1243,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1812,"Mod":0,"Key":256,"Ch":120},{"Timestamp":2683,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3018,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3033,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3050,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3067,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3084,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3100,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3363,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3499,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3628,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":3908,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4051,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4259,"Mod":0,"Key":258,"Ch":0},{"Timestamp":4883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5124,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5355,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6083,"Mod":0,"Key":258,"Ch":0},{"Timestamp":6563,"Mod":0,"Key":258,"Ch":0},{"Timestamp":7210,"Mod":0,"Key":258,"Ch":0},{"Timestamp":9475,"Mod":0,"Key":258,"Ch":0},{"Timestamp":10395,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11019,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11346,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11587,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":11883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12003,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12132,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12268,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12395,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12539,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12667,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12804,"Mod":0,"Key":258,"Ch":0},{"Timestamp":12947,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13075,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13211,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13347,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13475,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13620,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13771,"Mod":0,"Key":258,"Ch":0},{"Timestamp":13883,"Mod":0,"Key":258,"Ch":0},{"Timestamp":14027,"Mod":0,"Key":258,"Ch":0},{"Timestamp":14405,"Mod":0,"Key":27,"Ch":0},{"Timestamp":15540,"Mod":0,"Key":258,"Ch":0},{"Timestamp":15995,"Mod":0,"Key":256,"Ch":110},{"Timestamp":17267,"Mod":0,"Key":256,"Ch":108},{"Timestamp":17396,"Mod":0,"Key":256,"Ch":111},{"Timestamp":17547,"Mod":0,"Key":256,"Ch":108},{"Timestamp":17675,"Mod":0,"Key":13,"Ch":13},{"Timestamp":20195,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]} \ No newline at end of file diff --git a/test/integration/commitsNewBranch/setup.sh b/test/integration/commitsNewBranch/setup.sh deleted file mode 100644 index 4e35cf543..000000000 --- a/test/integration/commitsNewBranch/setup.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -set -e - -cd $1 - -git init - -git config user.email "CI@example.com" -git config user.name "CI" - -echo test0 > file0 -git add . -git commit -am file0 - -echo test1 > file1 -git add . -git commit -am file1 - -echo test2 > file2 -git add . -git commit -am file2 diff --git a/test/integration/commitsNewBranch/test.json b/test/integration/commitsNewBranch/test.json deleted file mode 100644 index d760dcc6c..000000000 --- a/test/integration/commitsNewBranch/test.json +++ /dev/null @@ -1 +0,0 @@ -{ "description": "Reverting a commit. Note here that our snapshot test fails if the commit SHA is included in the message hence the renaming of the revert commit after creating it", "speed": 20 } diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration_new/branch/suggestions/expected/repo/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..8a744b4fe --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/COMMIT_EDITMSG @@ -0,0 +1 @@ +my commit message diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/FETCH_HEAD b/test/integration_new/branch/suggestions/expected/repo/.git_keep/FETCH_HEAD similarity index 100% rename from test/integration/branchSuggestions/expected/repo/.git_keep/FETCH_HEAD rename to test/integration_new/branch/suggestions/expected/repo/.git_keep/FETCH_HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/HEAD b/test/integration_new/branch/suggestions/expected/repo/.git_keep/HEAD new file mode 100644 index 000000000..3b627c921 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/HEAD @@ -0,0 +1 @@ +ref: refs/heads/branch-to-checkout diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/config b/test/integration_new/branch/suggestions/expected/repo/.git_keep/config similarity index 100% rename from test/integration/branchSuggestions/expected/repo/.git_keep/config rename to test/integration_new/branch/suggestions/expected/repo/.git_keep/config diff --git a/test/integration/branchSuggestions/expected/repo/.git_keep/description b/test/integration_new/branch/suggestions/expected/repo/.git_keep/description similarity index 100% rename from test/integration/branchSuggestions/expected/repo/.git_keep/description rename to test/integration_new/branch/suggestions/expected/repo/.git_keep/description diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/index b/test/integration_new/branch/suggestions/expected/repo/.git_keep/index new file mode 100644 index 0000000000000000000000000000000000000000..65d675154f23ffb2d0196e017d44a5e7017550f5 GIT binary patch literal 65 zcmZ?q402{*U|<4bhL9jvS0E+HV4z^Y<=qr}%;|LA&IJiiy? 1659873850 +1000 commit (initial): my commit message +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from master to new-branch +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from new-branch to new-branch-2 +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from new-branch-2 to new-branch-3 +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from new-branch-3 to branch-to-checkout +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from branch-to-checkout to other-new-branch-2 +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from other-new-branch-2 to other-new-branch-3 +1682dc1949e1937af44b5270fec5c1ac9256c6a1 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 checkout: moving from other-new-branch-3 to branch-to-checkout diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/branch-to-checkout b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/branch-to-checkout new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/branch-to-checkout @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..7475970dc --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 commit (initial): my commit message diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-2 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/new-branch-3 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-2 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-2 new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-2 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-3 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-3 new file mode 100644 index 000000000..6b7fc9713 --- /dev/null +++ b/test/integration_new/branch/suggestions/expected/repo/.git_keep/logs/refs/heads/other-new-branch-3 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 1682dc1949e1937af44b5270fec5c1ac9256c6a1 CI 1659873850 +1000 branch: Created from HEAD diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/16/82dc1949e1937af44b5270fec5c1ac9256c6a1 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/16/82dc1949e1937af44b5270fec5c1ac9256c6a1 new file mode 100644 index 0000000000000000000000000000000000000000..45c339c1251d56ba83c8e0a054ad32b23844eecc GIT binary patch literal 125 zcmV-@0D}K`0ga783c@fD06pgwdlzIAlXU|kLQj20l5SOSNogarzqjBAybd!=sj9CA zHqfEXBH%N-Fck8xWRe^c5=!uSQ!|^bqp}61OnP?WQ+% literal 0 HcmV?d00001 diff --git a/test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/test/integration_new/branch/suggestions/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 new file mode 100644 index 0000000000000000000000000000000000000000..adf64119a33d7621aeeaa505d30adb58afaa5559 GIT binary patch literal 15 Wcmb 1659863059 +1000 commit (initial): my commit message diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..cc260688d --- /dev/null +++ b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 460150760ff1f381c3f5769b919cb73107c5871a CI 1659863059 +1000 commit (initial): my commit message diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/objects/3a/e2df795236e3c84cb1faa242d3268838603515 b/test/integration_new/commit/commit/expected/repo/.git_keep/objects/3a/e2df795236e3c84cb1faa242d3268838603515 new file mode 100644 index 0000000000000000000000000000000000000000..57198442f6ad83fffd10538fa84ecee53ee8f023 GIT binary patch literal 76 zcmV-S0JHyi0V^p=O;s?nWH2-^Ff%bx$gNDv%t>WfyEIKS{qBcl{tT_-wHN?jA|1xZp&@wy literal 0 HcmV?d00001 diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a b/test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a new file mode 100644 index 0000000000000000000000000000000000000000..4792ad56dc3321f51e627c8d96a72613f74b3d3a GIT binary patch literal 125 zcmV-@0D}K`0ga783c@fD06pgwdlw|hcGm<%gr540HQlPpwtXNW4u3-|1T^uoTN|@Lq1# 1659865912 +1000 commit (initial): commit 1 +470038e1336649b2965305f9f6a82501a836810e c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 commit: commit 2 +c8bec8f2b323cbb476e708bd10c145ea7cc9f726 62a60693a2e154e745ee353f67a05156d0532c23 CI 1659865912 +1000 commit: commit 3 +62a60693a2e154e745ee353f67a05156d0532c23 c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 checkout: moving from master to my-branch-name diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..57ad94e9f --- /dev/null +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 470038e1336649b2965305f9f6a82501a836810e CI 1659865912 +1000 commit (initial): commit 1 +470038e1336649b2965305f9f6a82501a836810e c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 commit: commit 2 +c8bec8f2b323cbb476e708bd10c145ea7cc9f726 62a60693a2e154e745ee353f67a05156d0532c23 CI 1659865912 +1000 commit: commit 3 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name new file mode 100644 index 000000000..7e8364dfe --- /dev/null +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 branch: Created from c8bec8f2b323cbb476e708bd10c145ea7cc9f726 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e b/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e new file mode 100644 index 0000000000000000000000000000000000000000..5099d76b85b09a9905b8b7a4b4cf9a4b0763ae96 GIT binary patch literal 118 zcmV-+0Ez#20ga783c@fD06pgwdlzIAHtPmNgr540Y`R6k5GfISeskypJj`Vnn9^!} zb?|gO_00@OCB&sLg`#YlGBR2uU1i}EL{>GUgyizKKidY=47X{1*e}=5+b*TmJ9v&s Y20p;qBcdC2OuGG&IKcC@LXw$*%4J%^%=d{%C4^tLaaj7@!$qw_9<#Y;>Ds^ zAG9QqT$78Yy!(CKVVYr_=9_*>+n#!nwOzr+km \ No newline at end of file diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master new file mode 100644 index 000000000..5c73ff150 --- /dev/null +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master @@ -0,0 +1 @@ +62a60693a2e154e745ee353f67a05156d0532c23 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name new file mode 100644 index 000000000..75da99d74 --- /dev/null +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name @@ -0,0 +1 @@ +c8bec8f2b323cbb476e708bd10c145ea7cc9f726 diff --git a/test/runner/main.go b/test/runner/main.go index 58ccd591f..ea6af59e8 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/assert" ) +// Deprecated: This file is part of the old way of doing things. See test/runner_new/main.go for the new way + // see docs/Integration_Tests.md // This file can be invoked directly, but you might find it easier to go through // test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. diff --git a/test/runner_new/main.go b/test/runner_new/main.go new file mode 100644 index 000000000..19a5dba03 --- /dev/null +++ b/test/runner_new/main.go @@ -0,0 +1,76 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "testing" + + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/stretchr/testify/assert" +) + +// see docs/Integration_Tests.md +// This file can be invoked directly, but you might find it easier to go through +// test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. +// +// If invoked directly, you can specify a test by passing it as the first argument. +// You can also specify that you want to record a test by passing MODE=record +// as an env var. + +func main() { + mode := integration.GetModeFromEnv() + includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" + selectedTestName := os.Args[1] + + // check if our given test name actually exists + if selectedTestName != "" { + found := false + for _, test := range integration.Tests { + if test.Name() == selectedTestName { + found = true + break + } + } + if !found { + log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests.go`?", selectedTestName) + } + } + + err := integration.RunTestsNew( + log.Printf, + runCmdInTerminal, + func(test types.Test, f func(*testing.T) error) { + if selectedTestName != "" && test.Name() != selectedTestName { + return + } + if err := f(nil); err != nil { + log.Print(err.Error()) + } + }, + mode, + func(_t *testing.T, expected string, actual string, prefix string) { //nolint:thelper + assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) + }, + includeSkipped, + ) + if err != nil { + log.Print(err.Error()) + } +} + +type MockTestingT struct{} + +func (t MockTestingT) Errorf(format string, args ...interface{}) { + fmt.Printf(format, args...) +} + +func runCmdInTerminal(cmd *exec.Cmd) error { + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + + return cmd.Run() +} diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index 03d3b3adc..0c76bfee7 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -108,6 +108,8 @@ const ( NORMAL PlayMode = iota RECORDING REPLAYING + // for the new form of integration tests + REPLAYING_NEW ) type Recording struct { @@ -116,8 +118,8 @@ type Recording struct { } type replayedEvents struct { - keys chan *TcellKeyEventWrapper - resizes chan *TcellResizeEventWrapper + Keys chan *TcellKeyEventWrapper + Resizes chan *TcellResizeEventWrapper } type RecordingConfig struct { @@ -216,10 +218,10 @@ func NewGui(mode OutputMode, supportOverlaps bool, playMode PlayMode, headless b KeyEvents: []*TcellKeyEventWrapper{}, ResizeEvents: []*TcellResizeEventWrapper{}, } - } else if playMode == REPLAYING { + } else if playMode == REPLAYING || playMode == REPLAYING_NEW { g.ReplayedEvents = replayedEvents{ - keys: make(chan *TcellKeyEventWrapper), - resizes: make(chan *TcellResizeEventWrapper), + Keys: make(chan *TcellKeyEventWrapper), + Resizes: make(chan *TcellResizeEventWrapper), } } @@ -1420,7 +1422,7 @@ func (g *Gui) replayRecording() { case <-ticker.C: timeWaited += 1 if timeWaited >= timeToWait { - g.ReplayedEvents.keys <- event + g.ReplayedEvents.Keys <- event break middle } case <-g.stop: @@ -1453,7 +1455,7 @@ func (g *Gui) replayRecording() { case <-ticker.C: timeWaited += 1 if timeWaited >= timeToWait { - g.ReplayedEvents.resizes <- event + g.ReplayedEvents.Resizes <- event break middle2 } case <-g.stop: diff --git a/vendor/github.com/jesseduffield/gocui/tcell_driver.go b/vendor/github.com/jesseduffield/gocui/tcell_driver.go index c5555e30d..81d30fe91 100644 --- a/vendor/github.com/jesseduffield/gocui/tcell_driver.go +++ b/vendor/github.com/jesseduffield/gocui/tcell_driver.go @@ -232,11 +232,11 @@ func (g *Gui) timeSinceStart() int64 { // pollEvent get tcell.Event and transform it into gocuiEvent func (g *Gui) pollEvent() GocuiEvent { var tev tcell.Event - if g.PlayMode == REPLAYING { + if g.PlayMode == REPLAYING || g.PlayMode == REPLAYING_NEW { select { - case ev := <-g.ReplayedEvents.keys: + case ev := <-g.ReplayedEvents.Keys: tev = (ev).toTcellEvent() - case ev := <-g.ReplayedEvents.resizes: + case ev := <-g.ReplayedEvents.Resizes: tev = (ev).toTcellEvent() } } else { From 225c563c630e8771c2c6741c78e8a427b3283f58 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 8 Aug 2022 21:32:58 +1000 Subject: [PATCH 02/16] another integration test --- pkg/gui/assert.go | 23 +++- pkg/gui/input.go | 107 +++++++++++++++--- pkg/gui/test_mode.go | 11 +- pkg/integration/env.go | 16 +++ .../integration_tests/branch/suggestions.go | 4 +- .../integration_tests/commit/commit.go | 2 +- .../integration_tests/commit/new_branch.go | 2 +- .../interactive_rebase/one.go | 41 +++++++ pkg/integration/integration_tests/tests.go | 3 + pkg/integration/shell.go | 22 ++++ pkg/integration/types/types.go | 22 +++- .../expected/repo/.git_keep/COMMIT_EDITMSG | 30 +++++ .../one/expected/repo/.git_keep/FETCH_HEAD | 0 .../one/expected/repo/.git_keep/HEAD | 1 + .../one/expected/repo/.git_keep/ORIG_HEAD | 1 + .../one/expected/repo/.git_keep/REBASE_HEAD | 1 + .../one/expected/repo/.git_keep/config | 10 ++ .../one/expected/repo/.git_keep/description | 1 + .../one/expected/repo/.git_keep/index | Bin 0 -> 460 bytes .../one/expected/repo/.git_keep/info/exclude | 7 ++ .../one/expected/repo/.git_keep/logs/HEAD | 10 ++ .../repo/.git_keep/logs/refs/heads/master | 6 + .../06/47fe4b7302efbfb235b8f0681b592cc3389d36 | Bin 0 -> 30 bytes .../35/da65f29bc0b48aa80bd3a02cff623cf4355fd3 | Bin 0 -> 133 bytes .../3b/f868a389d0073e715e848f0ee33d71064539ca | Bin 0 -> 30 bytes .../47/d78ad7a27fc7fe483389512ebf7ea34c5514bc | Bin 0 -> 30 bytes .../55/3197193920043fb04f3e39e825916990955204 | Bin 0 -> 55 bytes .../67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d | 3 + .../84/b1ae9d83049341897c9388afffdc9049c3317f | 3 + .../9c/68b57ac7b652fbebc5e93a9a1b72014400c269 | 2 + .../a0/2c4b36b68df7081152282cf1aabcab7b24e69b | Bin 0 -> 81 bytes .../a1/a6f7bda6aeaa08ec75f590845780fde90d901c | Bin 0 -> 117 bytes .../aa/2585aff7d2278341ca816f187e623503d7c4fb | 4 + .../c2/55cf4ef7fd5661a9d68b717243a978e42b05ac | Bin 0 -> 30 bytes .../cb/7e56856ecee89fa44c613e094fcf962fe18cf1 | Bin 0 -> 148 bytes .../e2/1978e5aaff3752bdeeb635c1667ec59c5bbde1 | Bin 0 -> 160 bytes .../e6/db1f58c2bb5ead41049a8ef3910360eead21e2 | Bin 0 -> 108 bytes .../e9/380a3c752e4b7c7e754fc402ce52302795a95a | Bin 0 -> 134 bytes .../f2/c01a881661486f147e47f5be82914c5d0c0030 | Bin 0 -> 30 bytes .../f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 | Bin 0 -> 153 bytes .../expected/repo/.git_keep/refs/heads/master | 1 + .../one/expected/repo/file01.txt | 1 + .../one/expected/repo/file02.txt | 1 + .../one/expected/repo/file03.txt | 1 + .../one/expected/repo/file05.txt | 1 + vendor/github.com/jesseduffield/gocui/view.go | 15 +++ 46 files changed, 326 insertions(+), 26 deletions(-) create mode 100644 pkg/integration/integration_tests/interactive_rebase/one.go create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/config create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/description create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/index create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/info/exclude create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/HEAD create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/06/47fe4b7302efbfb235b8f0681b592cc3389d36 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/35/da65f29bc0b48aa80bd3a02cff623cf4355fd3 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/3b/f868a389d0073e715e848f0ee33d71064539ca create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/47/d78ad7a27fc7fe483389512ebf7ea34c5514bc create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/55/3197193920043fb04f3e39e825916990955204 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a0/2c4b36b68df7081152282cf1aabcab7b24e69b create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/c2/55cf4ef7fd5661a9d68b717243a978e42b05ac create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cb/7e56856ecee89fa44c613e094fcf962fe18cf1 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e2/1978e5aaff3752bdeeb635c1667ec59c5bbde1 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e6/db1f58c2bb5ead41049a8ef3910360eead21e2 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e9/380a3c752e4b7c7e754fc402ce52302795a95a create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f2/c01a881661486f147e47f5be82914c5d0c0030 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/file01.txt create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/file02.txt create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/file03.txt create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/file05.txt diff --git a/pkg/gui/assert.go b/pkg/gui/assert.go index 43ecc26d8..253278220 100644 --- a/pkg/gui/assert.go +++ b/pkg/gui/assert.go @@ -2,8 +2,10 @@ package gui import ( "fmt" + "strings" "time" + guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -67,8 +69,23 @@ func (self *AssertImpl) CurrentBranchName(expectedViewName string) { }) } +func (self *AssertImpl) InListContext() { + self.assertWithRetries(func() (bool, string) { + currentContext := self.gui.currentContext() + _, ok := currentContext.(guiTypes.IListContext) + return ok, fmt.Sprintf("Expected current context to be a list context, but got %s", currentContext.GetKey()) + }) +} + +func (self *AssertImpl) SelectedLineContains(text string) { + self.assertWithRetries(func() (bool, string) { + line := self.gui.currentContext().GetView().SelectedLine() + return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) + }) +} + func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { - waitTimes := []int{0, 100, 200, 400, 800, 1600} + waitTimes := []int{0, 1, 5, 10, 200, 500, 1000} var message string for _, waitTime := range waitTimes { @@ -81,6 +98,10 @@ func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { } } + self.Fail(message) +} + +func (self *AssertImpl) Fail(message string) { self.gui.g.Close() // need to give the gui time to close time.Sleep(time.Millisecond * 100) diff --git a/pkg/gui/input.go b/pkg/gui/input.go index c6424c077..111d0de8e 100644 --- a/pkg/gui/input.go +++ b/pkg/gui/input.go @@ -1,29 +1,45 @@ package gui import ( + "fmt" + "strings" "time" "github.com/gdamore/tcell/v2" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/integration/types" ) type InputImpl struct { - g *gocui.Gui - keys config.KeybindingConfig + gui *Gui + keys config.KeybindingConfig + assert types.Assert + pushKeyDelay int +} + +func NewInputImpl(gui *Gui, keys config.KeybindingConfig, assert types.Assert, pushKeyDelay int) *InputImpl { + return &InputImpl{ + gui: gui, + keys: keys, + assert: assert, + pushKeyDelay: pushKeyDelay, + } } var _ types.Input = &InputImpl{} -func (self *InputImpl) PushKeys(keyStrs ...string) { +func (self *InputImpl) PressKeys(keyStrs ...string) { for _, keyStr := range keyStrs { - self.pushKey(keyStr) + self.pressKey(keyStr) } } -func (self *InputImpl) pushKey(keyStr string) { +func (self *InputImpl) pressKey(keyStr string) { + self.Wait(self.pushKeyDelay) + key := keybindings.GetKey(keyStr) var r rune @@ -36,58 +52,115 @@ func (self *InputImpl) pushKey(keyStr string) { tcellKey = tcell.Key(v) } - self.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( + self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( tcell.NewEventKey(tcellKey, r, tcell.ModNone), 0, ) } func (self *InputImpl) SwitchToStatusWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[0]) + self.pressKey(self.keys.Universal.JumpToBlock[0]) } func (self *InputImpl) SwitchToFilesWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[1]) + self.pressKey(self.keys.Universal.JumpToBlock[1]) } func (self *InputImpl) SwitchToBranchesWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[2]) + self.pressKey(self.keys.Universal.JumpToBlock[2]) } func (self *InputImpl) SwitchToCommitsWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[3]) + self.pressKey(self.keys.Universal.JumpToBlock[3]) } func (self *InputImpl) SwitchToStashWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[4]) + self.pressKey(self.keys.Universal.JumpToBlock[4]) } func (self *InputImpl) Type(content string) { for _, char := range content { - self.pushKey(string(char)) + self.pressKey(string(char)) } } func (self *InputImpl) Confirm() { - self.pushKey(self.keys.Universal.Confirm) + self.pressKey(self.keys.Universal.Confirm) } func (self *InputImpl) Cancel() { - self.pushKey(self.keys.Universal.Return) + self.pressKey(self.keys.Universal.Return) } func (self *InputImpl) Select() { - self.pushKey(self.keys.Universal.Select) + self.pressKey(self.keys.Universal.Select) } func (self *InputImpl) NextItem() { - self.pushKey(self.keys.Universal.NextItem) + self.pressKey(self.keys.Universal.NextItem) } func (self *InputImpl) PreviousItem() { - self.pushKey(self.keys.Universal.PrevItem) + self.pressKey(self.keys.Universal.PrevItem) +} + +func (self *InputImpl) ContinueMerge() { + self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) + self.assert.SelectedLineContains("continue") + self.Confirm() +} + +func (self *InputImpl) ContinueRebase() { + self.ContinueMerge() } func (self *InputImpl) Wait(milliseconds int) { time.Sleep(time.Duration(milliseconds) * time.Millisecond) } + +func (self *InputImpl) log(message string) { + self.gui.c.LogAction(message) +} + +// NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. +// If this changes in future, we'll need to update this code to first attempt to find the item +// in the current page and failing that, jump to the top of the view and iterate through all of it, +// looking for the item. +func (self *InputImpl) NavigateToListItemContainingText(text string) { + self.assert.InListContext() + + currentContext := self.gui.currentContext().(guiTypes.IListContext) + view := currentContext.GetView() + + // first we look for a duplicate on the current screen. We won't bother looking beyond that though. + matchCount := 0 + matchIndex := -1 + for i, line := range view.ViewBufferLines() { + if strings.Contains(line, text) { + matchCount++ + matchIndex = i + } + } + if matchCount > 1 { + self.assert.Fail(fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text)) + } + if matchCount == 1 { + selectedLineIdx := view.SelectedLineIdx() + if selectedLineIdx == matchIndex { + return + } + if selectedLineIdx < matchIndex { + for i := selectedLineIdx; i < matchIndex; i++ { + self.NextItem() + } + return + } else { + for i := selectedLineIdx; i > matchIndex; i-- { + self.PreviousItem() + } + return + } + } + + self.assert.Fail(fmt.Sprintf("Could not find item containing text: %s", text)) +} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index 88e42e440..7f57a4be8 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -21,10 +21,15 @@ func (gui *Gui) handleTestMode() { go func() { time.Sleep(time.Millisecond * 100) + shell := &integration.ShellImpl{} + assert := &AssertImpl{gui: gui} + keys := gui.Config.GetUserConfig().Keybinding + input := NewInputImpl(gui, keys, assert, integration.KeyPressDelay()) + test.Run( - &integration.ShellImpl{}, - &InputImpl{g: gui.g, keys: gui.Config.GetUserConfig().Keybinding}, - &AssertImpl{gui: gui}, + shell, + input, + assert, gui.c.UserConfig.Keybinding, ) diff --git a/pkg/integration/env.go b/pkg/integration/env.go index 7cdc72267..0c0d0b196 100644 --- a/pkg/integration/env.go +++ b/pkg/integration/env.go @@ -2,6 +2,7 @@ package integration import ( "os" + "strconv" "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/integration/types" @@ -31,6 +32,21 @@ func PlayingIntegrationTest() bool { return IntegrationTestName() != "" } +// this is the delay in milliseconds between keypresses +// defaults to zero +func KeyPressDelay() int { + delayStr := os.Getenv("KEY_PRESS_DELAY") + if delayStr == "" { + return 0 + } + + delay, err := strconv.Atoi(delayStr) + if err != nil { + panic(err) + } + return delay +} + // OLD integration test format stuff func Replaying() bool { diff --git a/pkg/integration/integration_tests/branch/suggestions.go b/pkg/integration/integration_tests/branch/suggestions.go index ff37ec9be..47c360514 100644 --- a/pkg/integration/integration_tests/branch/suggestions.go +++ b/pkg/integration/integration_tests/branch/suggestions.go @@ -23,12 +23,12 @@ var Suggestions = types.NewTest(types.NewTestArgs{ Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() - input.PushKeys(keys.Branches.CheckoutBranchByName) + input.PressKeys(keys.Branches.CheckoutBranchByName) assert.CurrentViewName("confirmation") input.Type("branch-to") - input.PushKeys(keys.Universal.TogglePanel) + input.PressKeys(keys.Universal.TogglePanel) assert.CurrentViewName("suggestions") // we expect the first suggestion to be the branch we want because it most diff --git a/pkg/integration/integration_tests/commit/commit.go b/pkg/integration/integration_tests/commit/commit.go index 777653bc0..f0af9462a 100644 --- a/pkg/integration/integration_tests/commit/commit.go +++ b/pkg/integration/integration_tests/commit/commit.go @@ -20,7 +20,7 @@ var Commit = types.NewTest(types.NewTestArgs{ input.Select() input.NextItem() input.Select() - input.PushKeys(keys.Files.CommitChanges) + input.PressKeys(keys.Files.CommitChanges) commitMessage := "my commit message" input.Type(commitMessage) diff --git a/pkg/integration/integration_tests/commit/new_branch.go b/pkg/integration/integration_tests/commit/new_branch.go index d70515280..218e95180 100644 --- a/pkg/integration/integration_tests/commit/new_branch.go +++ b/pkg/integration/integration_tests/commit/new_branch.go @@ -23,7 +23,7 @@ var NewBranch = types.NewTest(types.NewTestArgs{ assert.CurrentViewName("commits") input.NextItem() - input.PushKeys(keys.Universal.New) + input.PressKeys(keys.Universal.New) assert.CurrentViewName("confirmation") diff --git a/pkg/integration/integration_tests/interactive_rebase/one.go b/pkg/integration/integration_tests/interactive_rebase/one.go new file mode 100644 index 000000000..92780b24d --- /dev/null +++ b/pkg/integration/integration_tests/interactive_rebase/one.go @@ -0,0 +1,41 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +var One = types.NewTest(types.NewTestArgs{ + Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell types.Shell) { + shell. + CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 + }, + Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + input.SwitchToCommitsWindow() + assert.CurrentViewName("commits") + + input.NavigateToListItemContainingText("commit 02") + input.PressKeys(keys.Universal.Edit) + assert.SelectedLineContains("YOU ARE HERE") + + input.PreviousItem() + input.PressKeys(keys.Commits.MarkCommitAsFixup) + assert.SelectedLineContains("fixup") + + input.PreviousItem() + input.PressKeys(keys.Universal.Remove) + assert.SelectedLineContains("drop") + + input.PreviousItem() + input.PressKeys(keys.Commits.SquashDown) + assert.SelectedLineContains("squash") + + input.ContinueRebase() + + assert.CommitCount(2) + }, +}) diff --git a/pkg/integration/integration_tests/tests.go b/pkg/integration/integration_tests/tests.go index 1470ef178..752929698 100644 --- a/pkg/integration/integration_tests/tests.go +++ b/pkg/integration/integration_tests/tests.go @@ -3,6 +3,8 @@ package integration_tests import ( "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/branch" "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/commit" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/interactive_rebase" + "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -13,4 +15,5 @@ var Tests = []types.Test{ commit.Commit, commit.NewBranch, branch.Suggestions, + interactive_rebase.One, } diff --git a/pkg/integration/shell.go b/pkg/integration/shell.go index 69bccb66c..70da9c27b 100644 --- a/pkg/integration/shell.go +++ b/pkg/integration/shell.go @@ -40,6 +40,10 @@ func (s *ShellImpl) NewBranch(name string) types.Shell { return s.RunCommand("git checkout -b " + name) } +func (s *ShellImpl) GitAdd(path string) types.Shell { + return s.RunCommand(fmt.Sprintf("git add \"%s\"", path)) +} + func (s *ShellImpl) GitAddAll() types.Shell { return s.RunCommand("git add -A") } @@ -51,3 +55,21 @@ func (s *ShellImpl) Commit(message string) types.Shell { func (s *ShellImpl) EmptyCommit(message string) types.Shell { return s.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message)) } + +func (s *ShellImpl) CreateFileAndAdd(fileName string, fileContents string) types.Shell { + return s. + CreateFile(fileName, fileContents). + GitAdd(fileName) +} + +func (s *ShellImpl) CreateNCommits(n int) types.Shell { + for i := 1; i <= n; i++ { + s.CreateFileAndAdd( + fmt.Sprintf("file%02d.txt", i), + fmt.Sprintf("file%02d content", i), + ). + Commit(fmt.Sprintf("commit %02d", i)) + } + + return s +} diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 29b303f35..60fd4f353 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -31,9 +31,16 @@ type Shell interface { RunCommand(command string) Shell CreateFile(name string, content string) Shell NewBranch(branchName string) Shell + GitAdd(path string) Shell GitAddAll() Shell Commit(message string) Shell EmptyCommit(message string) Shell + // convenience method for creating a file and adding it + CreateFileAndAdd(fileName string, fileContents string) Shell + // creates commits 01, 02, 03, ..., n with a new file in each + // The reason for padding with zeroes is so that it's easier to do string + // matches on the commit messages when there are many of them + CreateNCommits(n int) Shell } // through this interface our test interacts with the lazygit gui @@ -41,7 +48,7 @@ type Shell interface { type Input interface { // key is something like 'w' or ''. It's best not to pass a direct value, // but instead to go through the default user config to get a more meaningful key name - PushKeys(keys ...string) + PressKeys(keys ...string) // for typing into a popup prompt Type(content string) // for when you want to allow lazygit to process something before continuing @@ -62,6 +69,15 @@ type Input interface { NextItem() // i.e. pressing up arrow PreviousItem() + // this will look for a list item in the current panel and if it finds it, it will + // enter the keypresses required to navigate to it. + // The test will fail if: + // - the user is not in a list item + // - no list item is found containing the given text + // - multiple list items are found containing the given text in the initial page of items + NavigateToListItemContainingText(text string) + ContinueRebase() + ContinueMerge() } // through this interface we assert on the state of the lazygit gui @@ -71,6 +87,10 @@ type Assert interface { HeadCommitMessage(string) CurrentViewName(expectedViewName string) CurrentBranchName(expectedBranchName string) + InListContext() + SelectedLineContains(text string) + // for when you just want to fail the test yourself + Fail(errorMessage string) } type TestImpl struct { diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..9e0c89102 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG @@ -0,0 +1,30 @@ +# This is a combination of 3 commits. +# This is the 1st commit message: + +commit 02 + +# The commit message #2 will be skipped: + +# commit 03 + +# This is the commit message #3: + +commit 05 + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# Date: Mon Aug 8 21:32:34 2022 +1000 +# +# interactive rebase in progress; onto a1a6f7b +# Last commands done (4 commands done): +# drop 84b1ae9 commit 04 +# squash aa2585a commit 05 +# No commands remaining. +# You are currently rebasing branch 'master' on 'a1a6f7b'. +# +# Changes to be committed: +# new file: file02.txt +# new file: file03.txt +# new file: file05.txt +# diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD new file mode 100644 index 000000000..42745976b --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD @@ -0,0 +1 @@ +aa2585aff7d2278341ca816f187e623503d7c4fb diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD new file mode 100644 index 000000000..42745976b --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD @@ -0,0 +1 @@ +aa2585aff7d2278341ca816f187e623503d7c4fb diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/config b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/config new file mode 100644 index 000000000..8ae104545 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/config @@ -0,0 +1,10 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[user] + email = CI@example.com + name = CI diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/description b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/index b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/index new file mode 100644 index 0000000000000000000000000000000000000000..ce7858b100125bd62c61344d33363fcaadd1dd38 GIT binary patch literal 460 zcmZ?q402{*U|<4bmZT3~g5-}_=fG%21_oB<>(7H37#f!_Ffe`vsu2NVKKJWg*B8|v z|L0-c8K}3vZn00O$Q}l+w9K4T14F%%iV~n2FaVkp%+yyJ38SIrT|+mI&HbNuG1L3~ zn@o3n$dHcIIczc43~HVcx_L6oru+bz2cn_oy+k+9`bWm%&I{~zg>fzYe2;Al*<3A8 zLCrHpH&0g5^ghTu5Dhi&AG&#mLeKks{~MOL@>+Lck@L!mC)%uQpyrt(n->z~>I(F@ zB!h{90oO|lE}K$4@0z+&|07K2f(+EBu8azD4Fv_ZGdQSBz*-Fr49rYS7z`Moj86xo tI>Zt^@ 1659958354 +1000 commit (initial): commit 01 +a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958354 +1000 commit: commit 02 +cb7e56856ecee89fa44c613e094fcf962fe18cf1 6741ab4fe22a3d36b6c64397fc4295dbae1ba71d CI 1659958354 +1000 commit: commit 03 +6741ab4fe22a3d36b6c64397fc4295dbae1ba71d 84b1ae9d83049341897c9388afffdc9049c3317f CI 1659958354 +1000 commit: commit 04 +84b1ae9d83049341897c9388afffdc9049c3317f aa2585aff7d2278341ca816f187e623503d7c4fb CI 1659958354 +1000 commit: commit 05 +aa2585aff7d2278341ca816f187e623503d7c4fb a1a6f7bda6aeaa08ec75f590845780fde90d901c CI 1659958355 +1000 rebase (start): checkout a1a6f7bda6aeaa08ec75f590845780fde90d901c +a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958355 +1000 rebase: fast-forward +cb7e56856ecee89fa44c613e094fcf962fe18cf1 9c68b57ac7b652fbebc5e93a9a1b72014400c269 CI 1659958355 +1000 rebase (continue) (fixup): # This is a combination of 2 commits. +9c68b57ac7b652fbebc5e93a9a1b72014400c269 f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (squash): commit 02 +f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (finish): returning to refs/heads/master diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..89309e282 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1,6 @@ +0000000000000000000000000000000000000000 a1a6f7bda6aeaa08ec75f590845780fde90d901c CI 1659958354 +1000 commit (initial): commit 01 +a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958354 +1000 commit: commit 02 +cb7e56856ecee89fa44c613e094fcf962fe18cf1 6741ab4fe22a3d36b6c64397fc4295dbae1ba71d CI 1659958354 +1000 commit: commit 03 +6741ab4fe22a3d36b6c64397fc4295dbae1ba71d 84b1ae9d83049341897c9388afffdc9049c3317f CI 1659958354 +1000 commit: commit 04 +84b1ae9d83049341897c9388afffdc9049c3317f aa2585aff7d2278341ca816f187e623503d7c4fb CI 1659958354 +1000 commit: commit 05 +aa2585aff7d2278341ca816f187e623503d7c4fb f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (finish): refs/heads/master onto a1a6f7bda6aeaa08ec75f590845780fde90d901c diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/06/47fe4b7302efbfb235b8f0681b592cc3389d36 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/06/47fe4b7302efbfb235b8f0681b592cc3389d36 new file mode 100644 index 0000000000000000000000000000000000000000..a8a2b586df771ca14cecde7807a27ce30a6b580c GIT binary patch literal 30 mcmb+k8|g>J5cLD&o#S2XU literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/35/da65f29bc0b48aa80bd3a02cff623cf4355fd3 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/35/da65f29bc0b48aa80bd3a02cff623cf4355fd3 new file mode 100644 index 0000000000000000000000000000000000000000..350af2800533c3104a701dce1729c5212dcef1f0 GIT binary patch literal 133 zcmV;00DAv;0V^p=O;s>7HDWL{FfcPQQAo?oNi{IkE2$`9aKGMleNp}Ke;&r2fqMJv z7W;&X>_Jv#1Xji7{?EIZ>HYpqraL}lNJr`%wwP;%tjZXy%KAsf;?4`~c7<^*{d|vY n3)x&PPa&%^0jv6SK&nG5(Ia1^&i(7YrinhWJPZZ^CcZiEHDN%H literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/3b/f868a389d0073e715e848f0ee33d71064539ca b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/3b/f868a389d0073e715e848f0ee33d71064539ca new file mode 100644 index 0000000000000000000000000000000000000000..07b07e91fc3473d2d2b21397ee9c3197bcd0bbe6 GIT binary patch literal 30 mcmbJsk8|g>J$%4di(1Z N`-F<@0RUnl5mQh97fApB literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d new file mode 100644 index 000000000..7aeda0bfd --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d @@ -0,0 +1,3 @@ +xÎM +Â0†a×9Åì™ÉŸºê1’ô +Ö–ÁãÛ…pûò,ÞºÌó£“h<ô Ä©H ©ÚRòä…½æ„æTØEÆÞ¬Àš5oxuªåŒSˆ¨@Ò–½¯QX}«M£mT›˜üî÷e£a¤Ë0ÞðÉóúÄ©.ó•$Õ\ðtf6{ݧ:þä?OìÌ;ß \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f new file mode 100644 index 000000000..cd394caaf --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f @@ -0,0 +1,3 @@ +xÎM +Â0@a×9ÅìI2?m@DèÊcL’ +Ö–ÁãÛ…pûø¯,óüè’úfÈU…[L¹øL£êèsEõ±´&K#dnݪ›½:È@A35‹Q±¢d)B˜†V(&®Y-dBuúî÷eƒéçévµÎëÓNe™/„Sâ™à¼÷n¯ûT·?ùσ'÷Ìu; \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 new file mode 100644 index 000000000..c97722a50 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 @@ -0,0 +1,2 @@ +x…ŽÁJÄ0†=ç)z–IÚ´‰È²°§½û“fbÃ6mi"úøF© ^„9}óý3ÿ¸¦ ´ª{(;3pï ÚŒÊ9Íä;‰%áµÛ¹2%Y‰v^ +¤> ÎSOL„†ÇAmÑtz0<[ôå(è­Lë×<_oþ ´Í|×tÙkkµiuE¥µTát}袗)f¨CP³..TâºÀ@}z,Ÿ~Yeb¹+Hœ3½ò“8*ñmóïqžÁ1ä{Ü6ö5ÔüXØŠOz d× \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a0/2c4b36b68df7081152282cf1aabcab7b24e69b b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a0/2c4b36b68df7081152282cf1aabcab7b24e69b new file mode 100644 index 0000000000000000000000000000000000000000..85866acd897b150150557b0a331c827562ca4fa7 GIT binary patch literal 81 zcmV-X0IvUd0V^p=O;s>AV=y!@Ff%bxNXyJgH89jGsVHG^zut9yQT_3M9>$%4di(1Z n`-F<@K~`l1R>kK2&%2oE{r*j+J3eGcN9r85m}>?A;$I(9i#8>| literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c new file mode 100644 index 0000000000000000000000000000000000000000..5405907585e20ec827c780274551074deb299a40 GIT binary patch literal 117 zcmV-*0E+*30gcT;3c@fDMq$@E#q0%{$z*DNC{pOE$EanBf+12OcznC)0o*=5cuOti z*+^18wauN(RB#n2B6HI9&S_Owc#a0?0ESuQ6y{HR)Q!f8`f(ckJD2y%FH0>qlFk6j XYKhLm%wezlr1_ux@Wc{+m!m5<*%dn* literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb new file mode 100644 index 000000000..9a3b44f36 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb @@ -0,0 +1,4 @@ +xÎA +1 …a×=E÷‚4“¦m@D˜•Çh3) +Ö† +ß.<€ÛÿÁ“µµG·ÀáÐwU«pLJ9׊‘¦²¨–€$BT!*ÃÀly×W·ÉÈÊKBç=$ŽÂ˜Ò˜×Ex  B¬&¿û}Ýí|³çùvÕOnÛSO²¶‹…@Ì”¼=‚sÎ §ºþ™ÿzëÈ|²);= \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/c2/55cf4ef7fd5661a9d68b717243a978e42b05ac b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/c2/55cf4ef7fd5661a9d68b717243a978e42b05ac new file mode 100644 index 0000000000000000000000000000000000000000..6ac1f71b1ac5591558972d0ca7dba18052afb32b GIT binary patch literal 30 mcmbJz`X~Q+Kx;nDg*%Rv CfJjCF literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e2/1978e5aaff3752bdeeb635c1667ec59c5bbde1 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e2/1978e5aaff3752bdeeb635c1667ec59c5bbde1 new file mode 100644 index 0000000000000000000000000000000000000000..37f59fe0fb0228d08de7eb36a278ec52c722aec2 GIT binary patch literal 160 zcmV;R0AK%j0V^p=O;s?oG+;0^FfcPQQAo?oNi{IkE2$`9aKGMleNp}Ke;&r2fqMJv z7W;&X>_Jv#1Xji7{?EIZ>HYpqraL}lNJr`%wwP;%tjZXy%KAsf;?4`~c7<^*{d|vY z3)x&PPa&%^0jv6SK&nG5(Ia1^&i(7YrinhWJPZcNs!YME4uzig`~Ej9apkq{!XoFD O6;HHT*8l*@%}K*azEJT1 literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e6/db1f58c2bb5ead41049a8ef3910360eead21e2 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e6/db1f58c2bb5ead41049a8ef3910360eead21e2 new file mode 100644 index 0000000000000000000000000000000000000000..8bcfafeb644d88961fada612e53598966fbe73ed GIT binary patch literal 108 zcmV-y0F(cC0V^p=O;s>7G+{6_FfcPQQAo?oNi{IkE2$`9aKGMleNp}Ke;&r2fqMJv z7W;&X>_Jv#1Xji7{?EIZ>HYpqraL}lNJr`%wwP;%tjZXy%KAsf;?4`~c7<^*{d|vY O3)x&PPXPdnMJ)ztA2U4w literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e9/380a3c752e4b7c7e754fc402ce52302795a95a b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/e9/380a3c752e4b7c7e754fc402ce52302795a95a new file mode 100644 index 0000000000000000000000000000000000000000..a75ffaf355a29646a7b0f684b160a9f68578835f GIT binary patch literal 134 zcmV;10D1p-0V^p=O;s>7HDWL{FfcPQQAo?oNi{IkE2$`9aKGMleNp}Ke;&r2fqMJv z7W;&X>_Jv#1Xji7{?EIZ>HYpqraL}lNJr`%wwP;%tjZXy%KAsf;?4`~c7<^*{d|vY o3)x&PPa&%^1*}4$*SZUfoL5#n(Pmu(07PRv+Jvq_VE_OC literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f2/c01a881661486f147e47f5be82914c5d0c0030 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f2/c01a881661486f147e47f5be82914c5d0c0030 new file mode 100644 index 0000000000000000000000000000000000000000..7e30b2e352ac316b3082ec42a9aabada4e057a17 GIT binary patch literal 30 mcmbJBbIibx)-tE3@FivnDr>lOXu1`JXrCz`eloCZx0ge_CO>g_DrhjzW_1y<_=7(gy H01`e0?axPZ literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master new file mode 100644 index 000000000..4190292c7 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master @@ -0,0 +1 @@ +f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/file01.txt b/test/integration_new/interactive_rebase/one/expected/repo/file01.txt new file mode 100644 index 000000000..47d78ad7a --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/file01.txt @@ -0,0 +1 @@ +file01 content \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/file02.txt b/test/integration_new/interactive_rebase/one/expected/repo/file02.txt new file mode 100644 index 000000000..0647fe4b7 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/file02.txt @@ -0,0 +1 @@ +file02 content \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/file03.txt b/test/integration_new/interactive_rebase/one/expected/repo/file03.txt new file mode 100644 index 000000000..3bf868a38 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/file03.txt @@ -0,0 +1 @@ +file03 content \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/file05.txt b/test/integration_new/interactive_rebase/one/expected/repo/file05.txt new file mode 100644 index 000000000..c255cf4ef --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/file05.txt @@ -0,0 +1 @@ +file05 content \ No newline at end of file diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 8ec290176..95c7ef4b1 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -465,6 +465,14 @@ func (v *View) Cursor() (x, y int) { return v.cx, v.cy } +func (v *View) CursorX() int { + return v.cx +} + +func (v *View) CursorY() int { + return v.cy +} + // SetOrigin sets the origin position of the view's internal buffer, // so the buffer starts to be printed from this point, which means that // it is linked with the origin point of view. It can be used to @@ -1235,6 +1243,13 @@ func (v *View) SelectedLineIdx() int { return seletedLineIdx } +// expected to only be used in tests +func (v *View) SelectedLine() string { + line := v.lines[v.SelectedLineIdx()] + str := lineType(line).String() + return strings.Replace(str, "\x00", " ", -1) +} + func (v *View) SelectedPoint() (int, int) { cx, cy := v.Cursor() ox, oy := v.Origin() From 46ae55f91e4feab67b01fcd63631dbaf47b3665f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 9 Aug 2022 20:27:44 +1000 Subject: [PATCH 03/16] introduce gui adapter --- main.go | 198 +++++------------- pkg/app/app.go | 8 +- pkg/app/run.go | 163 ++++++++++++++ pkg/config/app_config.go | 2 - pkg/gui/gui_adapter_impl.go | 73 +++++++ pkg/gui/services/custom_commands/resolver.go | 31 +++ pkg/gui/test_mode.go | 16 +- pkg/integration/env.go | 27 +-- pkg/integration/helpers/assert.go | 106 ++++++++++ pkg/integration/helpers/input.go | 153 ++++++++++++++ pkg/integration/{ => helpers}/shell.go | 2 +- pkg/integration/helpers/test_impl.go | 107 ++++++++++ pkg/integration/integration.go | 8 +- .../integration_tests/branch/suggestions.go | 3 +- .../integration_tests/commit/commit.go | 3 +- .../integration_tests/commit/new_branch.go | 3 +- .../interactive_rebase/one.go | 3 +- pkg/integration/types/types.go | 103 ++------- test/runner/main.go | 26 ++- test/{runner_new => runner_old}/main.go | 25 +-- 20 files changed, 763 insertions(+), 297 deletions(-) create mode 100644 pkg/app/run.go create mode 100644 pkg/gui/gui_adapter_impl.go create mode 100644 pkg/integration/helpers/assert.go create mode 100644 pkg/integration/helpers/input.go rename pkg/integration/{ => helpers}/shell.go (98%) create mode 100644 pkg/integration/helpers/test_impl.go rename test/{runner_new => runner_old}/main.go (70%) diff --git a/main.go b/main.go index 651d2be09..8684b8cd6 100644 --- a/main.go +++ b/main.go @@ -1,30 +1,19 @@ package main import ( - "bytes" - "fmt" - "log" "os" - "path/filepath" - "runtime" "runtime/debug" - "strings" "github.com/integrii/flaggy" "github.com/jesseduffield/lazygit/pkg/app" - "github.com/jesseduffield/lazygit/pkg/app/daemon" - "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/env" - "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/logs" "github.com/jesseduffield/lazygit/pkg/utils" - yaml "github.com/jesseduffield/yaml" "github.com/samber/lo" ) const DEFAULT_VERSION = "unversioned" +// These values may be set by the build script. +// we'll overwrite them if they haven't been set by the build script and if Go itself has set corresponding values in the binary var ( commit string version = DEFAULT_VERSION @@ -33,8 +22,13 @@ var ( ) func main() { - updateBuildInfo() + cliArgs := parseCliArgsAndEnvVars() + buildInfo := getBuildInfo() + app.Start(cliArgs, buildInfo, nil) +} + +func parseCliArgsAndEnvVars() *app.CliArgs { flaggy.DefaultParser.ShowVersionWithVersionFlag = false repoPath := "" @@ -46,20 +40,20 @@ func main() { gitArg := "" flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.") - versionFlag := false - flaggy.Bool(&versionFlag, "v", "version", "Print the current version") + printVersionInfo := false + flaggy.Bool(&printVersionInfo, "v", "version", "Print the current version") - debuggingFlag := false - flaggy.Bool(&debuggingFlag, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)") + debug := false + flaggy.Bool(&debug, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)") - logFlag := false - flaggy.Bool(&logFlag, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)") + tailLogs := false + flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)") - configFlag := false - flaggy.Bool(&configFlag, "c", "config", "Print the default config") + printDefaultConfig := false + flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config") - configDirFlag := false - flaggy.Bool(&configDirFlag, "cd", "print-config-dir", "Print the config directory") + printConfigDir := false + flaggy.Bool(&printConfigDir, "cd", "print-config-dir", "Print the config directory") useConfigDir := "" flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory") @@ -70,158 +64,68 @@ func main() { gitDir := "" flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument") - customConfig := "" - flaggy.String(&customConfig, "ucf", "use-config-file", "Comma separated list to custom config file(s)") + customConfigFile := "" + flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)") flaggy.Parse() if os.Getenv("DEBUG") == "TRUE" { - debuggingFlag = true + debug = true } - if repoPath != "" { - if workTree != "" || gitDir != "" { - log.Fatal("--path option is incompatible with the --work-tree and --git-dir options") - } - - absRepoPath, err := filepath.Abs(repoPath) - if err != nil { - log.Fatal(err) - } - workTree = absRepoPath - gitDir = filepath.Join(absRepoPath, ".git") + return &app.CliArgs{ + RepoPath: repoPath, + FilterPath: filterPath, + GitArg: gitArg, + PrintVersionInfo: printVersionInfo, + Debug: debug, + TailLogs: tailLogs, + PrintDefaultConfig: printDefaultConfig, + PrintConfigDir: printConfigDir, + UseConfigDir: useConfigDir, + WorkTree: workTree, + GitDir: gitDir, + CustomConfigFile: customConfigFile, } - - if customConfig != "" { - os.Setenv("LG_CONFIG_FILE", customConfig) - } - - if useConfigDir != "" { - os.Setenv("CONFIG_DIR", useConfigDir) - } - - if workTree != "" { - env.SetGitWorkTreeEnv(workTree) - } - - if gitDir != "" { - env.SetGitDirEnv(gitDir) - } - - if versionFlag { - fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", commit, date, buildSource, version, runtime.GOOS, runtime.GOARCH) - os.Exit(0) - } - - if configFlag { - var buf bytes.Buffer - encoder := yaml.NewEncoder(&buf) - err := encoder.Encode(config.GetDefaultConfig()) - if err != nil { - log.Fatal(err.Error()) - } - fmt.Printf("%s\n", buf.String()) - os.Exit(0) - } - - if configDirFlag { - fmt.Printf("%s\n", config.ConfigDir()) - os.Exit(0) - } - - if logFlag { - logs.TailLogs() - os.Exit(0) - } - - if workTree != "" { - if err := os.Chdir(workTree); err != nil { - log.Fatal(err.Error()) - } - } - - tempDir, err := os.MkdirTemp("", "lazygit-*") - if err != nil { - log.Fatal(err.Error()) - } - defer os.RemoveAll(tempDir) - - appConfig, err := config.NewAppConfig("lazygit", version, commit, date, buildSource, debuggingFlag, tempDir) - if err != nil { - log.Fatal(err.Error()) - } - - if test, ok := integration.CurrentIntegrationTest(); ok { - test.SetupConfig(appConfig) - } - - common, err := app.NewCommon(appConfig) - if err != nil { - log.Fatal(err) - } - - if daemon.InDaemonMode() { - daemon.Handle(common) - return - } - - parsedGitArg := parseGitArg(gitArg) - - app.Run(appConfig, common, types.NewStartArgs(filterPath, parsedGitArg)) } -func parseGitArg(gitArg string) types.GitArg { - typedArg := types.GitArg(gitArg) - - // using switch so that linter catches when a new git arg value is defined but not handled here - switch typedArg { - case types.GitArgNone, types.GitArgStatus, types.GitArgBranch, types.GitArgLog, types.GitArgStash: - return typedArg +func getBuildInfo() *app.BuildInfo { + buildInfo := &app.BuildInfo{ + Commit: commit, + Date: date, + Version: version, + BuildSource: buildSource, } - permittedValues := []string{ - string(types.GitArgStatus), - string(types.GitArgBranch), - string(types.GitArgLog), - string(types.GitArgStash), - } - - log.Fatalf("Invalid git arg value: '%s'. Must be one of the following values: %s. e.g. 'lazygit status'. See 'lazygit --help'.", - gitArg, - strings.Join(permittedValues, ", "), - ) - - panic("unreachable") -} - -func updateBuildInfo() { // if the version has already been set by build flags then we'll honour that. // chances are it's something like v0.31.0 which is more informative than a // commit hash. - if version != DEFAULT_VERSION { - return + if buildInfo.Version != DEFAULT_VERSION { + return buildInfo } - buildInfo, ok := debug.ReadBuildInfo() + goBuildInfo, ok := debug.ReadBuildInfo() if !ok { - return + return buildInfo } - revision, ok := lo.Find(buildInfo.Settings, func(setting debug.BuildSetting) bool { + revision, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { return setting.Key == "vcs.revision" }) if ok { - commit = revision.Value + buildInfo.Commit = revision.Value // if lazygit was built from source we'll show the version as the // abbreviated commit hash - version = utils.ShortSha(revision.Value) + buildInfo.Version = utils.ShortSha(revision.Value) } // if version hasn't been set we assume that neither has the date - time, ok := lo.Find(buildInfo.Settings, func(setting debug.BuildSetting) bool { + time, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { return setting.Key == "vcs.time" }) if ok { - date = time.Value + buildInfo.Date = time.Value } + + return buildInfo } diff --git a/pkg/app/app.go b/pkg/app/app.go index 418c1406e..36fbc3e97 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -24,6 +24,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/updates" ) @@ -38,7 +39,12 @@ type App struct { Updater *updates.Updater // may only need this on the Gui } -func Run(config config.AppConfigurer, common *common.Common, startArgs types.StartArgs) { +func Run( + config config.AppConfigurer, + common *common.Common, + startArgs types.StartArgs, + test integrationTypes.Test, +) { app, err := NewApp(config, common) if err == nil { diff --git a/pkg/app/run.go b/pkg/app/run.go new file mode 100644 index 000000000..aaa133792 --- /dev/null +++ b/pkg/app/run.go @@ -0,0 +1,163 @@ +package app + +import ( + "bytes" + "fmt" + "log" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/jesseduffield/lazygit/pkg/app/daemon" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/env" + "github.com/jesseduffield/lazygit/pkg/gui/types" + "github.com/jesseduffield/lazygit/pkg/integration" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/jesseduffield/lazygit/pkg/logs" + "gopkg.in/yaml.v3" +) + +type CliArgs struct { + RepoPath string + FilterPath string + GitArg string + PrintVersionInfo bool + Debug bool + TailLogs bool + PrintDefaultConfig bool + PrintConfigDir bool + UseConfigDir string + WorkTree string + GitDir string + CustomConfigFile string +} + +type BuildInfo struct { + Commit string + Date string + Version string + BuildSource string +} + +// only used when running integration tests +type TestConfig struct { + Test integrationTypes.Test +} + +func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { + if cliArgs.RepoPath != "" { + if cliArgs.WorkTree != "" || cliArgs.GitDir != "" { + log.Fatal("--path option is incompatible with the --work-tree and --git-dir options") + } + + absRepoPath, err := filepath.Abs(cliArgs.RepoPath) + if err != nil { + log.Fatal(err) + } + cliArgs.WorkTree = absRepoPath + cliArgs.GitDir = filepath.Join(absRepoPath, ".git") + } + + if cliArgs.CustomConfigFile != "" { + os.Setenv("LG_CONFIG_FILE", cliArgs.CustomConfigFile) + } + + if cliArgs.UseConfigDir != "" { + os.Setenv("CONFIG_DIR", cliArgs.UseConfigDir) + } + + if cliArgs.WorkTree != "" { + env.SetGitWorkTreeEnv(cliArgs.WorkTree) + } + + if cliArgs.GitDir != "" { + env.SetGitDirEnv(cliArgs.GitDir) + } + + if cliArgs.PrintVersionInfo { + fmt.Printf("commit=%s, build date=%s, build source=%s, version=%s, os=%s, arch=%s\n", buildInfo.Commit, buildInfo.Date, buildInfo.BuildSource, buildInfo.Version, runtime.GOOS, runtime.GOARCH) + os.Exit(0) + } + + if cliArgs.PrintDefaultConfig { + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + err := encoder.Encode(config.GetDefaultConfig()) + if err != nil { + log.Fatal(err.Error()) + } + fmt.Printf("%s\n", buf.String()) + os.Exit(0) + } + + if cliArgs.PrintConfigDir { + fmt.Printf("%s\n", config.ConfigDir()) + os.Exit(0) + } + + if cliArgs.TailLogs { + logs.TailLogs() + os.Exit(0) + } + + if cliArgs.WorkTree != "" { + if err := os.Chdir(cliArgs.WorkTree); err != nil { + log.Fatal(err.Error()) + } + } + + tempDir, err := os.MkdirTemp("", "lazygit-*") + if err != nil { + log.Fatal(err.Error()) + } + defer os.RemoveAll(tempDir) + + appConfig, err := config.NewAppConfig("lazygit", buildInfo.Version, buildInfo.Commit, buildInfo.Date, buildInfo.BuildSource, cliArgs.Debug, tempDir) + if err != nil { + log.Fatal(err.Error()) + } + + if test, ok := integration.CurrentIntegrationTest(); ok { + test.SetupConfig(appConfig) + } + + common, err := NewCommon(appConfig) + if err != nil { + log.Fatal(err) + } + + if daemon.InDaemonMode() { + daemon.Handle(common) + return + } + + parsedGitArg := parseGitArg(cliArgs.GitArg) + + Run(appConfig, common, types.NewStartArgs(cliArgs.FilterPath, parsedGitArg), test) +} + +func parseGitArg(gitArg string) types.GitArg { + typedArg := types.GitArg(gitArg) + + // using switch so that linter catches when a new git arg value is defined but not handled here + switch typedArg { + case types.GitArgNone, types.GitArgStatus, types.GitArgBranch, types.GitArgLog, types.GitArgStash: + return typedArg + } + + permittedValues := []string{ + string(types.GitArgStatus), + string(types.GitArgBranch), + string(types.GitArgLog), + string(types.GitArgStash), + } + + log.Fatalf("Invalid git arg value: '%s'. Must be one of the following values: %s. e.g. 'lazygit status'. See 'lazygit --help'.", + gitArg, + strings.Join(permittedValues, ", "), + ) + + panic("unreachable") +} diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 3591166c7..9806bcf58 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -27,8 +27,6 @@ type AppConfig struct { IsNewRepo bool } -// AppConfigurer interface allows individual app config structs to inherit Fields -// from AppConfig and still be used by lazygit. type AppConfigurer interface { GetDebug() bool diff --git a/pkg/gui/gui_adapter_impl.go b/pkg/gui/gui_adapter_impl.go new file mode 100644 index 000000000..dc811f92e --- /dev/null +++ b/pkg/gui/gui_adapter_impl.go @@ -0,0 +1,73 @@ +package gui + +import ( + "time" + + "github.com/gdamore/tcell/v2" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +// this gives our integration test a way of interacting with the gui for sending keypresses +// and reading state. +type GuiAdapterImpl struct { + gui *Gui +} + +var _ integrationTypes.GuiAdapter = &GuiAdapterImpl{} + +func (self *GuiAdapterImpl) PressKey(keyStr string) { + key := keybindings.GetKey(keyStr) + + var r rune + var tcellKey tcell.Key + switch v := key.(type) { + case rune: + r = v + tcellKey = tcell.KeyRune + case gocui.Key: + tcellKey = tcell.Key(v) + } + + self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( + tcell.NewEventKey(tcellKey, r, tcell.ModNone), + 0, + ) +} + +func (self *GuiAdapterImpl) Keys() config.KeybindingConfig { + return self.gui.Config.GetUserConfig().Keybinding +} + +func (self *GuiAdapterImpl) CurrentContext() types.Context { + return self.gui.c.CurrentContext() +} + +func (self *GuiAdapterImpl) Model() *types.Model { + return self.gui.State.Model +} + +func (self *GuiAdapterImpl) Fail(message string) { + self.gui.g.Close() + // need to give the gui time to close + time.Sleep(time.Millisecond * 100) + panic(message) +} + +// logs to the normal place that you log to i.e. viewable with `lazygit --logs` +func (self *GuiAdapterImpl) Log(message string) { + self.gui.c.Log.Warn(message) +} + +// logs in the actual UI (in the commands panel) +func (self *GuiAdapterImpl) LogUI(message string) { + self.gui.c.LogAction(message) +} + +func (self *GuiAdapterImpl) CheckedOutRef() *models.Branch { + return self.gui.helpers.Refs.GetCheckedOutRef() +} diff --git a/pkg/gui/services/custom_commands/resolver.go b/pkg/gui/services/custom_commands/resolver.go index 35ebbd9d1..1b80987c1 100644 --- a/pkg/gui/services/custom_commands/resolver.go +++ b/pkg/gui/services/custom_commands/resolver.go @@ -1,6 +1,10 @@ package custom_commands import ( + "bytes" + "fmt" + "text/template" + "github.com/jesseduffield/lazygit/pkg/common" "github.com/jesseduffield/lazygit/pkg/config" ) @@ -101,3 +105,30 @@ func (self *Resolver) resolveMenuOption(option *config.CustomCommandMenuOption, Value: value, }, nil } + +func main() { + fmt.Println(ResolveTemplate("old approach: {{index .PromptResponses 0}}, new approach: {{ .Form.a }}", CustomCommandObject{ + PromptResponses: []string{"a"}, + Form: map[string]string{"a": "B"}, + })) +} + +type CustomCommandObject struct { + // deprecated. Use Responses instead + PromptResponses []string + Form map[string]string +} + +func ResolveTemplate(templateStr string, object interface{}) (string, error) { + tmpl, err := template.New("template").Parse(templateStr) + if err != nil { + return "", err + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, object); err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index 7f57a4be8..0252aa198 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -10,6 +10,10 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) +type IntegrationTest interface { + Run(guiAdapter *GuiAdapterImpl) +} + func (gui *Gui) handleTestMode() { if integration.PlayingIntegrationTest() { test, ok := integration.CurrentIntegrationTest() @@ -21,17 +25,7 @@ func (gui *Gui) handleTestMode() { go func() { time.Sleep(time.Millisecond * 100) - shell := &integration.ShellImpl{} - assert := &AssertImpl{gui: gui} - keys := gui.Config.GetUserConfig().Keybinding - input := NewInputImpl(gui, keys, assert, integration.KeyPressDelay()) - - test.Run( - shell, - input, - assert, - gui.c.UserConfig.Keybinding, - ) + test.Run(&GuiAdapterImpl{gui: gui}) gui.g.Update(func(*gocui.Gui) error { return gocui.ErrQuit diff --git a/pkg/integration/env.go b/pkg/integration/env.go index 0c0d0b196..6102923fc 100644 --- a/pkg/integration/env.go +++ b/pkg/integration/env.go @@ -2,9 +2,9 @@ package integration import ( "os" - "strconv" "github.com/jesseduffield/generics/slices" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -18,35 +18,20 @@ func IntegrationTestName() string { return os.Getenv("LAZYGIT_TEST_NAME") } +func PlayingIntegrationTest() bool { + return IntegrationTestName() != "" +} + func CurrentIntegrationTest() (types.Test, bool) { if !PlayingIntegrationTest() { return nil, false } - return slices.Find(Tests, func(test types.Test) bool { + return slices.Find(integration_tests.Tests, func(test types.Test) bool { return test.Name() == IntegrationTestName() }) } -func PlayingIntegrationTest() bool { - return IntegrationTestName() != "" -} - -// this is the delay in milliseconds between keypresses -// defaults to zero -func KeyPressDelay() int { - delayStr := os.Getenv("KEY_PRESS_DELAY") - if delayStr == "" { - return 0 - } - - delay, err := strconv.Atoi(delayStr) - if err != nil { - panic(err) - } - return delay -} - // OLD integration test format stuff func Replaying() bool { diff --git a/pkg/integration/helpers/assert.go b/pkg/integration/helpers/assert.go new file mode 100644 index 000000000..eced1187d --- /dev/null +++ b/pkg/integration/helpers/assert.go @@ -0,0 +1,106 @@ +package helpers + +import ( + "fmt" + "strings" + "time" + + "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type AssertImpl struct { + gui integrationTypes.GuiAdapter +} + +var _ integrationTypes.Assert = &AssertImpl{} + +func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().Files) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d changed working tree files, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) CommitCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().Commits) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d commits present, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { + self.assertWithRetries(func() (bool, string) { + if len(self.gui.Model().Commits) == 0 { + return false, "Expected at least one commit to be present" + } + + headCommit := self.gui.Model().Commits[0] + if headCommit.Name != expectedMessage { + return false, fmt.Sprintf( + "Expected commit message to be '%s', but got '%s'", + expectedMessage, headCommit.Name, + ) + } + + return true, "" + }) +} + +func (self *AssertImpl) CurrentViewName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.CurrentContext().GetView().Name() + return actual == expectedViewName, fmt.Sprintf("Expected current view name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) CurrentBranchName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.CheckedOutRef().Name + return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) + }) +} + +func (self *AssertImpl) InListContext() { + self.assertWithRetries(func() (bool, string) { + currentContext := self.gui.CurrentContext() + _, ok := currentContext.(types.IListContext) + return ok, fmt.Sprintf("Expected current context to be a list context, but got %s", currentContext.GetKey()) + }) +} + +func (self *AssertImpl) SelectedLineContains(text string) { + self.assertWithRetries(func() (bool, string) { + line := self.gui.CurrentContext().GetView().SelectedLine() + return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) + }) +} + +func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { + waitTimes := []int{0, 1, 5, 10, 200, 500, 1000} + + var message string + for _, waitTime := range waitTimes { + time.Sleep(time.Duration(waitTime) * time.Millisecond) + + var ok bool + ok, message = test() + if ok { + return + } + } + + self.Fail(message) +} + +func (self *AssertImpl) Fail(message string) { + self.gui.Fail(message) +} diff --git a/pkg/integration/helpers/input.go b/pkg/integration/helpers/input.go new file mode 100644 index 000000000..fa4875ea1 --- /dev/null +++ b/pkg/integration/helpers/input.go @@ -0,0 +1,153 @@ +package helpers + +import ( + "fmt" + "strings" + "time" + + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type InputImpl struct { + gui integrationTypes.GuiAdapter + keys config.KeybindingConfig + assert integrationTypes.Assert + pushKeyDelay int +} + +func NewInputImpl(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert integrationTypes.Assert, pushKeyDelay int) *InputImpl { + return &InputImpl{ + gui: gui, + keys: keys, + assert: assert, + pushKeyDelay: pushKeyDelay, + } +} + +var _ integrationTypes.Input = &InputImpl{} + +func (self *InputImpl) PressKeys(keyStrs ...string) { + for _, keyStr := range keyStrs { + self.pressKey(keyStr) + } +} + +func (self *InputImpl) pressKey(keyStr string) { + self.Wait(self.pushKeyDelay) + + self.gui.PressKey(keyStr) +} + +func (self *InputImpl) SwitchToStatusWindow() { + self.pressKey(self.keys.Universal.JumpToBlock[0]) +} + +func (self *InputImpl) SwitchToFilesWindow() { + self.pressKey(self.keys.Universal.JumpToBlock[1]) +} + +func (self *InputImpl) SwitchToBranchesWindow() { + self.pressKey(self.keys.Universal.JumpToBlock[2]) +} + +func (self *InputImpl) SwitchToCommitsWindow() { + self.pressKey(self.keys.Universal.JumpToBlock[3]) +} + +func (self *InputImpl) SwitchToStashWindow() { + self.pressKey(self.keys.Universal.JumpToBlock[4]) +} + +func (self *InputImpl) Type(content string) { + for _, char := range content { + self.pressKey(string(char)) + } +} + +func (self *InputImpl) Confirm() { + self.pressKey(self.keys.Universal.Confirm) +} + +func (self *InputImpl) Cancel() { + self.pressKey(self.keys.Universal.Return) +} + +func (self *InputImpl) Select() { + self.pressKey(self.keys.Universal.Select) +} + +func (self *InputImpl) NextItem() { + self.pressKey(self.keys.Universal.NextItem) +} + +func (self *InputImpl) PreviousItem() { + self.pressKey(self.keys.Universal.PrevItem) +} + +func (self *InputImpl) ContinueMerge() { + self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) + self.assert.SelectedLineContains("continue") + self.Confirm() +} + +func (self *InputImpl) ContinueRebase() { + self.ContinueMerge() +} + +func (self *InputImpl) Wait(milliseconds int) { + time.Sleep(time.Duration(milliseconds) * time.Millisecond) +} + +func (self *InputImpl) LogUI(message string) { + self.gui.LogUI(message) +} + +func (self *InputImpl) Log(message string) { + self.gui.LogUI(message) +} + +// NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. +// If this changes in future, we'll need to update this code to first attempt to find the item +// in the current page and failing that, jump to the top of the view and iterate through all of it, +// looking for the item. +func (self *InputImpl) NavigateToListItemContainingText(text string) { + self.assert.InListContext() + + currentContext := self.gui.CurrentContext().(types.IListContext) + + view := currentContext.GetView() + + // first we look for a duplicate on the current screen. We won't bother looking beyond that though. + matchCount := 0 + matchIndex := -1 + for i, line := range view.ViewBufferLines() { + if strings.Contains(line, text) { + matchCount++ + matchIndex = i + } + } + if matchCount > 1 { + self.assert.Fail(fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text)) + } + if matchCount == 1 { + selectedLineIdx := view.SelectedLineIdx() + if selectedLineIdx == matchIndex { + return + } + if selectedLineIdx < matchIndex { + for i := selectedLineIdx; i < matchIndex; i++ { + self.NextItem() + } + return + } else { + for i := selectedLineIdx; i > matchIndex; i-- { + self.PreviousItem() + } + return + } + } + + self.assert.Fail(fmt.Sprintf("Could not find item containing text: %s", text)) +} diff --git a/pkg/integration/shell.go b/pkg/integration/helpers/shell.go similarity index 98% rename from pkg/integration/shell.go rename to pkg/integration/helpers/shell.go index 70da9c27b..fdfb0d231 100644 --- a/pkg/integration/shell.go +++ b/pkg/integration/helpers/shell.go @@ -1,4 +1,4 @@ -package integration +package helpers import ( "fmt" diff --git a/pkg/integration/helpers/test_impl.go b/pkg/integration/helpers/test_impl.go new file mode 100644 index 000000000..7d9efa56f --- /dev/null +++ b/pkg/integration/helpers/test_impl.go @@ -0,0 +1,107 @@ +package helpers + +import ( + "os" + "strconv" + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +type TestImpl struct { + name string + description string + extraCmdArgs string + skip bool + setupRepo func(shell types.Shell) + setupConfig func(config *config.AppConfig) + run func( + shell types.Shell, + input types.Input, + assert types.Assert, + keys config.KeybindingConfig, + ) +} + +type NewTestArgs struct { + Description string + SetupRepo func(shell types.Shell) + SetupConfig func(config *config.AppConfig) + Run func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) + ExtraCmdArgs string + Skip bool +} + +func NewTest(args NewTestArgs) *TestImpl { + return &TestImpl{ + name: testNameFromFilePath(), + description: args.Description, + extraCmdArgs: args.ExtraCmdArgs, + skip: args.Skip, + setupRepo: args.SetupRepo, + setupConfig: args.SetupConfig, + run: args.Run, + } +} + +var _ types.Test = (*TestImpl)(nil) + +func (self *TestImpl) Name() string { + return self.name +} + +func (self *TestImpl) Description() string { + return self.description +} + +func (self *TestImpl) ExtraCmdArgs() string { + return self.extraCmdArgs +} + +func (self *TestImpl) Skip() bool { + return self.skip +} + +func (self *TestImpl) SetupConfig(config *config.AppConfig) { + self.setupConfig(config) +} + +func (self *TestImpl) SetupRepo(shell types.Shell) { + self.setupRepo(shell) +} + +// I want access to all contexts, the model, the ability to press a key, the ability to log, +func (self *TestImpl) Run( + gui types.GuiAdapter, +) { + shell := &ShellImpl{} + assert := &AssertImpl{gui: gui} + keys := gui.Keys() + input := NewInputImpl(gui, keys, assert, KeyPressDelay()) + + self.run(shell, input, assert, keys) +} + +func testNameFromFilePath() string { + path := utils.FilePath(3) + name := strings.Split(path, "integration/integration_tests/")[1] + + return name[:len(name)-len(".go")] +} + +// this is the delay in milliseconds between keypresses +// defaults to zero +func KeyPressDelay() int { + delayStr := os.Getenv("KEY_PRESS_DELAY") + if delayStr == "" { + return 0 + } + + delay, err := strconv.Atoi(delayStr) + if err != nil { + panic(err) + } + return delay +} diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 853dae0d9..4119f47a3 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -10,15 +10,13 @@ import ( "testing" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" "github.com/jesseduffield/lazygit/pkg/integration/types" ) // this is the integration runner for the new and improved integration interface -// re-exporting this so that clients only need to import one package -var Tests = integration_tests.Tests - func RunTestsNew( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, @@ -41,7 +39,7 @@ func RunTestsNew( return err } - for _, test := range Tests { + for _, test := range integration_tests.Tests { test := test fnWrapper(test, func(t *testing.T) error { //nolint: thelper @@ -141,7 +139,7 @@ func createFixtureNew(test types.Test, actualDir string, rootDir string) error { panic(err) } - shell := &ShellImpl{} + shell := &helpers.ShellImpl{} shell.RunCommand("git init") shell.RunCommand(`git config user.email "CI@example.com"`) shell.RunCommand(`git config user.name "CI"`) diff --git a/pkg/integration/integration_tests/branch/suggestions.go b/pkg/integration/integration_tests/branch/suggestions.go index 47c360514..a925280e7 100644 --- a/pkg/integration/integration_tests/branch/suggestions.go +++ b/pkg/integration/integration_tests/branch/suggestions.go @@ -2,10 +2,11 @@ package branch import ( "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/types" ) -var Suggestions = types.NewTest(types.NewTestArgs{ +var Suggestions = helpers.NewTest(helpers.NewTestArgs{ Description: "Checking out a branch with name suggestions", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/integration_tests/commit/commit.go b/pkg/integration/integration_tests/commit/commit.go index f0af9462a..9fd3bb356 100644 --- a/pkg/integration/integration_tests/commit/commit.go +++ b/pkg/integration/integration_tests/commit/commit.go @@ -2,10 +2,11 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/types" ) -var Commit = types.NewTest(types.NewTestArgs{ +var Commit = helpers.NewTest(helpers.NewTestArgs{ Description: "Staging a couple files and committing", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/integration_tests/commit/new_branch.go b/pkg/integration/integration_tests/commit/new_branch.go index 218e95180..9669937fa 100644 --- a/pkg/integration/integration_tests/commit/new_branch.go +++ b/pkg/integration/integration_tests/commit/new_branch.go @@ -2,10 +2,11 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/types" ) -var NewBranch = types.NewTest(types.NewTestArgs{ +var NewBranch = helpers.NewTest(helpers.NewTestArgs{ Description: "Creating a new branch from a commit", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/integration_tests/interactive_rebase/one.go b/pkg/integration/integration_tests/interactive_rebase/one.go index 92780b24d..d8899569c 100644 --- a/pkg/integration/integration_tests/interactive_rebase/one.go +++ b/pkg/integration/integration_tests/interactive_rebase/one.go @@ -2,10 +2,11 @@ package interactive_rebase import ( "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/types" ) -var One = types.NewTest(types.NewTestArgs{ +var One = helpers.NewTest(helpers.NewTestArgs{ Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 60fd4f353..2e6ea34f6 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -1,12 +1,17 @@ package types import ( - "strings" - + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/jesseduffield/lazygit/pkg/gui/types" ) +// TODO: refactor this so that we don't have code spread around so much. We want +// our TestImpl struct to take the dependencies it needs from the gui and then +// create the input, assert, shell structs itself. That way, we can potentially +// ditch these interfaces so that we don't need to keep updating them every time +// we add a method to the concrete struct. + type Test interface { Name() string Description() string @@ -16,7 +21,7 @@ type Test interface { // so that they appear when lazygit runs SetupConfig(config *config.AppConfig) // this is called upon lazygit starting - Run(Shell, Input, Assert, config.KeybindingConfig) + Run(GuiAdapter) // e.g. '-debug' ExtraCmdArgs() string // for tests that are flakey and when we don't have time to fix them @@ -81,6 +86,7 @@ type Input interface { } // through this interface we assert on the state of the lazygit gui +// implementation is at pkg/gui/assert.go type Assert interface { WorkingTreeFileCount(int) CommitCount(int) @@ -93,80 +99,17 @@ type Assert interface { Fail(errorMessage string) } -type TestImpl struct { - name string - description string - extraCmdArgs string - skip bool - setupRepo func(shell Shell) - setupConfig func(config *config.AppConfig) - run func( - shell Shell, - input Input, - assert Assert, - keys config.KeybindingConfig, - ) -} - -type NewTestArgs struct { - Description string - SetupRepo func(shell Shell) - SetupConfig func(config *config.AppConfig) - Run func(shell Shell, input Input, assert Assert, keys config.KeybindingConfig) - ExtraCmdArgs string - Skip bool -} - -func NewTest(args NewTestArgs) *TestImpl { - return &TestImpl{ - name: testNameFromFilePath(), - description: args.Description, - extraCmdArgs: args.ExtraCmdArgs, - skip: args.Skip, - setupRepo: args.SetupRepo, - setupConfig: args.SetupConfig, - run: args.Run, - } -} - -var _ Test = (*TestImpl)(nil) - -func (self *TestImpl) Name() string { - return self.name -} - -func (self *TestImpl) Description() string { - return self.description -} - -func (self *TestImpl) ExtraCmdArgs() string { - return self.extraCmdArgs -} - -func (self *TestImpl) Skip() bool { - return self.skip -} - -func (self *TestImpl) SetupConfig(config *config.AppConfig) { - self.setupConfig(config) -} - -func (self *TestImpl) SetupRepo(shell Shell) { - self.setupRepo(shell) -} - -func (self *TestImpl) Run( - shell Shell, - input Input, - assert Assert, - keys config.KeybindingConfig, -) { - self.run(shell, input, assert, keys) -} - -func testNameFromFilePath() string { - path := utils.FilePath(3) - name := strings.Split(path, "integration/integration_tests/")[1] - - return name[:len(name)-len(".go")] +type GuiAdapter interface { + PressKey(string) + Keys() config.KeybindingConfig + CurrentContext() types.Context + Model() *types.Model + Fail(message string) + // These two log methods are for the sake of debugging while testing. There's no need to actually + // commit any logging. + // logs to the normal place that you log to i.e. viewable with `lazygit --logs` + Log(message string) + // logs in the actual UI (in the commands panel) + LogUI(message string) + CheckedOutRef() *models.Branch } diff --git a/test/runner/main.go b/test/runner/main.go index ea6af59e8..691f0de4f 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -8,11 +8,11 @@ import ( "testing" "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" + "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/stretchr/testify/assert" ) -// Deprecated: This file is part of the old way of doing things. See test/runner_new/main.go for the new way - // see docs/Integration_Tests.md // This file can be invoked directly, but you might find it easier to go through // test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. @@ -23,15 +23,28 @@ import ( func main() { mode := integration.GetModeFromEnv() - speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" selectedTestName := os.Args[1] - err := integration.RunTests( + // check if our given test name actually exists + if selectedTestName != "" { + found := false + for _, test := range integration_tests.Tests { + if test.Name() == selectedTestName { + found = true + break + } + } + if !found { + log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests.go`?", selectedTestName) + } + } + + err := integration.RunTestsNew( log.Printf, runCmdInTerminal, - func(test *integration.Test, f func(*testing.T) error) { - if selectedTestName != "" && test.Name != selectedTestName { + func(test types.Test, f func(*testing.T) error) { + if selectedTestName != "" && test.Name() != selectedTestName { return } if err := f(nil); err != nil { @@ -39,7 +52,6 @@ func main() { } }, mode, - speedEnv, func(_t *testing.T, expected string, actual string, prefix string) { //nolint:thelper assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) }, diff --git a/test/runner_new/main.go b/test/runner_old/main.go similarity index 70% rename from test/runner_new/main.go rename to test/runner_old/main.go index 19a5dba03..ea6af59e8 100644 --- a/test/runner_new/main.go +++ b/test/runner_old/main.go @@ -8,10 +8,11 @@ import ( "testing" "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/stretchr/testify/assert" ) +// Deprecated: This file is part of the old way of doing things. See test/runner_new/main.go for the new way + // see docs/Integration_Tests.md // This file can be invoked directly, but you might find it easier to go through // test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. @@ -22,28 +23,15 @@ import ( func main() { mode := integration.GetModeFromEnv() + speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" selectedTestName := os.Args[1] - // check if our given test name actually exists - if selectedTestName != "" { - found := false - for _, test := range integration.Tests { - if test.Name() == selectedTestName { - found = true - break - } - } - if !found { - log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests.go`?", selectedTestName) - } - } - - err := integration.RunTestsNew( + err := integration.RunTests( log.Printf, runCmdInTerminal, - func(test types.Test, f func(*testing.T) error) { - if selectedTestName != "" && test.Name() != selectedTestName { + func(test *integration.Test, f func(*testing.T) error) { + if selectedTestName != "" && test.Name != selectedTestName { return } if err := f(nil); err != nil { @@ -51,6 +39,7 @@ func main() { } }, mode, + speedEnv, func(_t *testing.T, expected string, actual string, prefix string) { //nolint:thelper assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) }, From d890238c7bcbdd62e7158df0c1f3f0e5c0b05b66 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 9 Aug 2022 21:11:41 +1000 Subject: [PATCH 04/16] move input and assert into integration tests package --- pkg/app/app.go | 2 - pkg/app/run.go | 5 +- pkg/gui/assert.go | 109 ------------------ pkg/gui/gui.go | 17 ++- pkg/gui/gui_adapter_impl.go | 3 +- pkg/gui/gui_test.go | 7 +- pkg/gui/input.go | 166 --------------------------- pkg/gui/test_mode.go | 88 +++++++++++--- pkg/gui/types/main_args.go | 5 +- pkg/gui/types/test.go | 27 +++++ pkg/integration/env.go | 18 --- pkg/integration/helpers/assert.go | 2 +- pkg/integration/helpers/input.go | 4 +- pkg/integration/helpers/test_impl.go | 5 +- pkg/integration/integration.go | 56 +++++---- pkg/integration/recording.go | 61 ---------- pkg/integration/types/types.go | 18 +-- test/runner/main.go | 7 +- 18 files changed, 164 insertions(+), 436 deletions(-) delete mode 100644 pkg/gui/assert.go delete mode 100644 pkg/gui/input.go create mode 100644 pkg/gui/types/test.go delete mode 100644 pkg/integration/recording.go diff --git a/pkg/app/app.go b/pkg/app/app.go index 36fbc3e97..a45ffb118 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -24,7 +24,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" - integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/updates" ) @@ -43,7 +42,6 @@ func Run( config config.AppConfigurer, common *common.Common, startArgs types.StartArgs, - test integrationTypes.Test, ) { app, err := NewApp(config, common) diff --git a/pkg/app/run.go b/pkg/app/run.go index aaa133792..b1e2c396f 100644 --- a/pkg/app/run.go +++ b/pkg/app/run.go @@ -13,7 +13,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/logs" "gopkg.in/yaml.v3" @@ -119,7 +118,7 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { log.Fatal(err.Error()) } - if test, ok := integration.CurrentIntegrationTest(); ok { + if test != nil { test.SetupConfig(appConfig) } @@ -135,7 +134,7 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { parsedGitArg := parseGitArg(cliArgs.GitArg) - Run(appConfig, common, types.NewStartArgs(cliArgs.FilterPath, parsedGitArg), test) + Run(appConfig, common, types.NewStartArgs(cliArgs.FilterPath, parsedGitArg, test)) } func parseGitArg(gitArg string) types.GitArg { diff --git a/pkg/gui/assert.go b/pkg/gui/assert.go deleted file mode 100644 index 253278220..000000000 --- a/pkg/gui/assert.go +++ /dev/null @@ -1,109 +0,0 @@ -package gui - -import ( - "fmt" - "strings" - "time" - - guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration/types" -) - -type AssertImpl struct { - gui *Gui -} - -var _ types.Assert = &AssertImpl{} - -func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { - self.assertWithRetries(func() (bool, string) { - actualCount := len(self.gui.State.Model.Files) - - return actualCount == expectedCount, fmt.Sprintf( - "Expected %d changed working tree files, but got %d", - expectedCount, actualCount, - ) - }) -} - -func (self *AssertImpl) CommitCount(expectedCount int) { - self.assertWithRetries(func() (bool, string) { - actualCount := len(self.gui.State.Model.Commits) - - return actualCount == expectedCount, fmt.Sprintf( - "Expected %d commits present, but got %d", - expectedCount, actualCount, - ) - }) -} - -func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { - self.assertWithRetries(func() (bool, string) { - if len(self.gui.State.Model.Commits) == 0 { - return false, "Expected at least one commit to be present" - } - - headCommit := self.gui.State.Model.Commits[0] - if headCommit.Name != expectedMessage { - return false, fmt.Sprintf( - "Expected commit message to be '%s', but got '%s'", - expectedMessage, headCommit.Name, - ) - } - - return true, "" - }) -} - -func (self *AssertImpl) CurrentViewName(expectedViewName string) { - self.assertWithRetries(func() (bool, string) { - actual := self.gui.currentViewName() - return actual == expectedViewName, fmt.Sprintf("Expected current view name to be '%s', but got '%s'", expectedViewName, actual) - }) -} - -func (self *AssertImpl) CurrentBranchName(expectedViewName string) { - self.assertWithRetries(func() (bool, string) { - actual := self.gui.helpers.Refs.GetCheckedOutRef().Name - return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) - }) -} - -func (self *AssertImpl) InListContext() { - self.assertWithRetries(func() (bool, string) { - currentContext := self.gui.currentContext() - _, ok := currentContext.(guiTypes.IListContext) - return ok, fmt.Sprintf("Expected current context to be a list context, but got %s", currentContext.GetKey()) - }) -} - -func (self *AssertImpl) SelectedLineContains(text string) { - self.assertWithRetries(func() (bool, string) { - line := self.gui.currentContext().GetView().SelectedLine() - return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) - }) -} - -func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { - waitTimes := []int{0, 1, 5, 10, 200, 500, 1000} - - var message string - for _, waitTime := range waitTimes { - time.Sleep(time.Duration(waitTime) * time.Millisecond) - - var ok bool - ok, message = test() - if ok { - return - } - } - - self.Fail(message) -} - -func (self *AssertImpl) Fail(message string) { - self.gui.g.Close() - // need to give the gui time to close - time.Sleep(time.Millisecond * 100) - panic(message) -} diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index f7c8926f5..11c8af78b 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -31,7 +31,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/tasks" "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/updates" @@ -418,14 +417,14 @@ var RuneReplacements = map[rune]string{ graph.CommitSymbol: "o", } -func (gui *Gui) initGocui(headless bool) (*gocui.Gui, error) { - recordEvents := integration.RecordingEvents() +func (gui *Gui) initGocui(headless bool, test types.Test) (*gocui.Gui, error) { + recordEvents := RecordingEvents() playMode := gocui.NORMAL if recordEvents { playMode = gocui.RECORDING - } else if integration.Replaying() { + } else if Replaying() { playMode = gocui.REPLAYING - } else if integration.IntegrationTestName() != "" { + } else if test != nil { playMode = gocui.REPLAYING_NEW } @@ -478,7 +477,7 @@ func (gui *Gui) viewTabMap() map[string][]context.TabView { // Run: setup the gui with keybindings and start the mainloop func (gui *Gui) Run(startArgs types.StartArgs) error { - g, err := gui.initGocui(integration.Headless()) + g, err := gui.initGocui(Headless(), startArgs.Test) if err != nil { return err } @@ -493,7 +492,7 @@ func (gui *Gui) Run(startArgs types.StartArgs) error { }) deadlock.Opts.Disable = !gui.Debug - gui.handleTestMode() + gui.handleTestMode(startArgs.Test) gui.g.OnSearchEscape = gui.onSearchEscape if err := gui.Config.ReloadUserConfig(); err != nil { @@ -580,7 +579,7 @@ func (gui *Gui) RunAndHandleError(startArgs types.StartArgs) error { } } - if err := integration.SaveRecording(gui.g.Recording); err != nil { + if err := SaveRecording(gui.g.Recording); err != nil { return err } @@ -614,7 +613,7 @@ func (gui *Gui) runSubprocessWithSuspense(subprocess oscommands.ICmdObj) (bool, gui.Mutexes.SubprocessMutex.Lock() defer gui.Mutexes.SubprocessMutex.Unlock() - if integration.Replaying() { + if Replaying() { // we do not yet support running subprocesses within integration tests. So if // we're replaying an integration test and we're inside this method, something // has gone wrong, so we should fail diff --git a/pkg/gui/gui_adapter_impl.go b/pkg/gui/gui_adapter_impl.go index dc811f92e..427b8eb47 100644 --- a/pkg/gui/gui_adapter_impl.go +++ b/pkg/gui/gui_adapter_impl.go @@ -9,7 +9,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/types" - integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) // this gives our integration test a way of interacting with the gui for sending keypresses @@ -18,7 +17,7 @@ type GuiAdapterImpl struct { gui *Gui } -var _ integrationTypes.GuiAdapter = &GuiAdapterImpl{} +var _ types.GuiAdapter = &GuiAdapterImpl{} func (self *GuiAdapterImpl) PressKey(keyStr string) { key := keybindings.GetKey(keyStr) diff --git a/pkg/gui/gui_test.go b/pkg/gui/gui_test.go index 2565392e6..2d698d34c 100644 --- a/pkg/gui/gui_test.go +++ b/pkg/gui/gui_test.go @@ -36,20 +36,19 @@ func Test(t *testing.T) { err := integration.RunTestsNew( t.Logf, runCmdHeadless, - func(test types.Test, f func(*testing.T) error) { + func(test types.Test, f func() error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return } t.Run(test.Name(), func(t *testing.T) { - err := f(t) + err := f() assert.NoError(t, err) }) }, mode, - func(t *testing.T, expected string, actual string, prefix string) { - t.Helper() + func(expected string, actual string, prefix string) { assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) }, includeSkipped, diff --git a/pkg/gui/input.go b/pkg/gui/input.go deleted file mode 100644 index 111d0de8e..000000000 --- a/pkg/gui/input.go +++ /dev/null @@ -1,166 +0,0 @@ -package gui - -import ( - "fmt" - "strings" - "time" - - "github.com/gdamore/tcell/v2" - "github.com/jesseduffield/gocui" - "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/gui/keybindings" - guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration/types" -) - -type InputImpl struct { - gui *Gui - keys config.KeybindingConfig - assert types.Assert - pushKeyDelay int -} - -func NewInputImpl(gui *Gui, keys config.KeybindingConfig, assert types.Assert, pushKeyDelay int) *InputImpl { - return &InputImpl{ - gui: gui, - keys: keys, - assert: assert, - pushKeyDelay: pushKeyDelay, - } -} - -var _ types.Input = &InputImpl{} - -func (self *InputImpl) PressKeys(keyStrs ...string) { - for _, keyStr := range keyStrs { - self.pressKey(keyStr) - } -} - -func (self *InputImpl) pressKey(keyStr string) { - self.Wait(self.pushKeyDelay) - - key := keybindings.GetKey(keyStr) - - var r rune - var tcellKey tcell.Key - switch v := key.(type) { - case rune: - r = v - tcellKey = tcell.KeyRune - case gocui.Key: - tcellKey = tcell.Key(v) - } - - self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( - tcell.NewEventKey(tcellKey, r, tcell.ModNone), - 0, - ) -} - -func (self *InputImpl) SwitchToStatusWindow() { - self.pressKey(self.keys.Universal.JumpToBlock[0]) -} - -func (self *InputImpl) SwitchToFilesWindow() { - self.pressKey(self.keys.Universal.JumpToBlock[1]) -} - -func (self *InputImpl) SwitchToBranchesWindow() { - self.pressKey(self.keys.Universal.JumpToBlock[2]) -} - -func (self *InputImpl) SwitchToCommitsWindow() { - self.pressKey(self.keys.Universal.JumpToBlock[3]) -} - -func (self *InputImpl) SwitchToStashWindow() { - self.pressKey(self.keys.Universal.JumpToBlock[4]) -} - -func (self *InputImpl) Type(content string) { - for _, char := range content { - self.pressKey(string(char)) - } -} - -func (self *InputImpl) Confirm() { - self.pressKey(self.keys.Universal.Confirm) -} - -func (self *InputImpl) Cancel() { - self.pressKey(self.keys.Universal.Return) -} - -func (self *InputImpl) Select() { - self.pressKey(self.keys.Universal.Select) -} - -func (self *InputImpl) NextItem() { - self.pressKey(self.keys.Universal.NextItem) -} - -func (self *InputImpl) PreviousItem() { - self.pressKey(self.keys.Universal.PrevItem) -} - -func (self *InputImpl) ContinueMerge() { - self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) - self.assert.SelectedLineContains("continue") - self.Confirm() -} - -func (self *InputImpl) ContinueRebase() { - self.ContinueMerge() -} - -func (self *InputImpl) Wait(milliseconds int) { - time.Sleep(time.Duration(milliseconds) * time.Millisecond) -} - -func (self *InputImpl) log(message string) { - self.gui.c.LogAction(message) -} - -// NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. -// If this changes in future, we'll need to update this code to first attempt to find the item -// in the current page and failing that, jump to the top of the view and iterate through all of it, -// looking for the item. -func (self *InputImpl) NavigateToListItemContainingText(text string) { - self.assert.InListContext() - - currentContext := self.gui.currentContext().(guiTypes.IListContext) - view := currentContext.GetView() - - // first we look for a duplicate on the current screen. We won't bother looking beyond that though. - matchCount := 0 - matchIndex := -1 - for i, line := range view.ViewBufferLines() { - if strings.Contains(line, text) { - matchCount++ - matchIndex = i - } - } - if matchCount > 1 { - self.assert.Fail(fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text)) - } - if matchCount == 1 { - selectedLineIdx := view.SelectedLineIdx() - if selectedLineIdx == matchIndex { - return - } - if selectedLineIdx < matchIndex { - for i := selectedLineIdx; i < matchIndex; i++ { - self.NextItem() - } - return - } else { - for i := selectedLineIdx; i > matchIndex; i-- { - self.PreviousItem() - } - return - } - } - - self.assert.Fail(fmt.Sprintf("Could not find item containing text: %s", text)) -} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index 0252aa198..942e7824e 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -1,12 +1,15 @@ package gui import ( - "fmt" + "encoding/json" + "io/ioutil" "log" + "os" + "strconv" "time" "github.com/jesseduffield/gocui" - "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" ) @@ -14,14 +17,8 @@ type IntegrationTest interface { Run(guiAdapter *GuiAdapterImpl) } -func (gui *Gui) handleTestMode() { - if integration.PlayingIntegrationTest() { - test, ok := integration.CurrentIntegrationTest() - - if !ok { - panic(fmt.Sprintf("test %s not found", integration.IntegrationTestName())) - } - +func (gui *Gui) handleTestMode(test types.Test) { + if test != nil { go func() { time.Sleep(time.Millisecond * 100) @@ -42,14 +39,14 @@ func (gui *Gui) handleTestMode() { }) } - if integration.Replaying() { + if Replaying() { gui.g.RecordingConfig = gocui.RecordingConfig{ - Speed: integration.GetRecordingSpeed(), + Speed: GetRecordingSpeed(), Leeway: 100, } var err error - gui.g.Recording, err = integration.LoadRecording() + gui.g.Recording, err = LoadRecording() if err != nil { panic(err) } @@ -60,3 +57,68 @@ func (gui *Gui) handleTestMode() { }) } } + +func Headless() bool { + return os.Getenv("HEADLESS") != "" +} + +// OLD integration test format stuff + +func Replaying() bool { + return os.Getenv("REPLAY_EVENTS_FROM") != "" +} + +func RecordingEvents() bool { + return recordEventsTo() != "" +} + +func recordEventsTo() string { + return os.Getenv("RECORD_EVENTS_TO") +} + +func GetRecordingSpeed() float64 { + // humans are slow so this speeds things up. + speed := 1.0 + envReplaySpeed := os.Getenv("SPEED") + if envReplaySpeed != "" { + var err error + speed, err = strconv.ParseFloat(envReplaySpeed, 64) + if err != nil { + log.Fatal(err) + } + } + return speed +} + +func LoadRecording() (*gocui.Recording, error) { + path := os.Getenv("REPLAY_EVENTS_FROM") + + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + recording := &gocui.Recording{} + + err = json.Unmarshal(data, &recording) + if err != nil { + return nil, err + } + + return recording, nil +} + +func SaveRecording(recording *gocui.Recording) error { + if !RecordingEvents() { + return nil + } + + jsonEvents, err := json.Marshal(recording) + if err != nil { + return err + } + + path := recordEventsTo() + + return ioutil.WriteFile(path, jsonEvents, 0o600) +} diff --git a/pkg/gui/types/main_args.go b/pkg/gui/types/main_args.go index b055b3736..7d9b9fbb7 100644 --- a/pkg/gui/types/main_args.go +++ b/pkg/gui/types/main_args.go @@ -6,6 +6,8 @@ type StartArgs struct { FilterPath string // GitArg determines what context we open in GitArg GitArg + // integration test (only relevant when invoking lazygit in the context of an integration test) + Test Test } type GitArg string @@ -18,9 +20,10 @@ const ( GitArgStash GitArg = "stash" ) -func NewStartArgs(filterPath string, gitArg GitArg) StartArgs { +func NewStartArgs(filterPath string, gitArg GitArg, test Test) StartArgs { return StartArgs{ FilterPath: filterPath, GitArg: gitArg, + Test: test, } } diff --git a/pkg/gui/types/test.go b/pkg/gui/types/test.go new file mode 100644 index 000000000..55c1d50a8 --- /dev/null +++ b/pkg/gui/types/test.go @@ -0,0 +1,27 @@ +package types + +import ( + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/config" +) + +type Test interface { + Run(GuiAdapter) + SetupConfig(config *config.AppConfig) +} + +// this is the interface through which our integration tests interact with the lazygit gui +type GuiAdapter interface { + PressKey(string) + Keys() config.KeybindingConfig + CurrentContext() Context + Model() *Model + Fail(message string) + // These two log methods are for the sake of debugging while testing. There's no need to actually + // commit any logging. + // logs to the normal place that you log to i.e. viewable with `lazygit --logs` + Log(message string) + // logs in the actual UI (in the commands panel) + LogUI(message string) + CheckedOutRef() *models.Branch +} diff --git a/pkg/integration/env.go b/pkg/integration/env.go index 6102923fc..89f25d85e 100644 --- a/pkg/integration/env.go +++ b/pkg/integration/env.go @@ -8,10 +8,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/types" ) -func Headless() bool { - return os.Getenv("HEADLESS") != "" -} - // NEW integration test format stuff func IntegrationTestName() string { @@ -31,17 +27,3 @@ func CurrentIntegrationTest() (types.Test, bool) { return test.Name() == IntegrationTestName() }) } - -// OLD integration test format stuff - -func Replaying() bool { - return os.Getenv("REPLAY_EVENTS_FROM") != "" -} - -func RecordingEvents() bool { - return recordEventsTo() != "" -} - -func recordEventsTo() string { - return os.Getenv("RECORD_EVENTS_TO") -} diff --git a/pkg/integration/helpers/assert.go b/pkg/integration/helpers/assert.go index eced1187d..491bca348 100644 --- a/pkg/integration/helpers/assert.go +++ b/pkg/integration/helpers/assert.go @@ -10,7 +10,7 @@ import ( ) type AssertImpl struct { - gui integrationTypes.GuiAdapter + gui types.GuiAdapter } var _ integrationTypes.Assert = &AssertImpl{} diff --git a/pkg/integration/helpers/input.go b/pkg/integration/helpers/input.go index fa4875ea1..f23a08688 100644 --- a/pkg/integration/helpers/input.go +++ b/pkg/integration/helpers/input.go @@ -11,13 +11,13 @@ import ( ) type InputImpl struct { - gui integrationTypes.GuiAdapter + gui types.GuiAdapter keys config.KeybindingConfig assert integrationTypes.Assert pushKeyDelay int } -func NewInputImpl(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert integrationTypes.Assert, pushKeyDelay int) *InputImpl { +func NewInputImpl(gui types.GuiAdapter, keys config.KeybindingConfig, assert integrationTypes.Assert, pushKeyDelay int) *InputImpl { return &InputImpl{ gui: gui, keys: keys, diff --git a/pkg/integration/helpers/test_impl.go b/pkg/integration/helpers/test_impl.go index 7d9efa56f..eaec83561 100644 --- a/pkg/integration/helpers/test_impl.go +++ b/pkg/integration/helpers/test_impl.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/jesseduffield/lazygit/pkg/config" + guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/utils" ) @@ -73,9 +74,7 @@ func (self *TestImpl) SetupRepo(shell types.Shell) { } // I want access to all contexts, the model, the ability to press a key, the ability to log, -func (self *TestImpl) Run( - gui types.GuiAdapter, -) { +func (self *TestImpl) Run(gui guiTypes.GuiAdapter) { shell := &ShellImpl{} assert := &AssertImpl{gui: gui} keys := gui.Keys() diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 4119f47a3..de440c4ea 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -7,8 +7,9 @@ import ( "os" "os/exec" "path/filepath" - "testing" + "runtime/debug" + "github.com/jesseduffield/lazygit/pkg/app" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" @@ -20,9 +21,9 @@ import ( func RunTestsNew( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test types.Test, f func(*testing.T) error), + fnWrapper func(test types.Test, f func() error), mode Mode, - onFail func(t *testing.T, expected string, actual string, prefix string), + onFail func(expected string, actual string, prefix string), includeSkipped bool, ) error { rootDir := GetRootDirectory() @@ -42,7 +43,7 @@ func RunTestsNew( for _, test := range integration_tests.Tests { test := test - fnWrapper(test, func(t *testing.T) error { //nolint: thelper + fnWrapper(test, func() error { //nolint: thelper if test.Skip() && !includeSkipped { logf("skipping test: %s", test.Name()) return nil @@ -65,12 +66,7 @@ func RunTestsNew( configDir := filepath.Join(testPath, "used_config") - cmd, err := getLazygitCommandNew(test, testPath, rootDir) - if err != nil { - return err - } - - err = runCmd(cmd) + err = runLazygit(test, testPath, rootDir) if err != nil { return err } @@ -120,7 +116,7 @@ func RunTestsNew( } logf("%s", string(bytes)) - onFail(t, expectedRepo, actualRepo, f.Name()) + onFail(expectedRepo, actualRepo, f.Name()) } } } @@ -154,9 +150,7 @@ func createFixtureNew(test types.Test, actualDir string, rootDir string) error { return nil } -func getLazygitCommandNew(test types.Test, testPath string, rootDir string) (*exec.Cmd, error) { - osCommand := oscommands.NewDummyOSCommand() - +func runLazygit(test types.Test, testPath string, rootDir string) error { templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") actualRepoDir := filepath.Join(testPath, "actual", "repo") @@ -164,18 +158,38 @@ func getLazygitCommandNew(test types.Test, testPath string, rootDir string) (*ex err := os.RemoveAll(configDir) if err != nil { - return nil, err + return err } err = oscommands.CopyDir(templateConfigDir, configDir) if err != nil { - return nil, err + return err } - cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, test.ExtraCmdArgs()) + // TODO: support test.ExtraCmdArgs in some form. + cliArgs := &app.CliArgs{ + Debug: true, + UseConfigDir: configDir, + RepoPath: actualRepoDir, + } - cmdObj := osCommand.Cmd.New(cmdStr) + buildInfo := &app.BuildInfo{ + Commit: "1234abc", + Date: "2020-01-01", + Version: "1.0.0", + BuildSource: "unknown", + } - cmdObj.AddEnvVars(fmt.Sprintf("LAZYGIT_TEST_NAME=%s", test.Name())) - - return cmdObj.GetCmd(), nil + return convertPanicToError(func() { app.Start(cliArgs, buildInfo, test) }) +} + +func convertPanicToError(f func()) (err error) { //nolint: nakedret + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Lazygit panicked. Stacktrace:: \n" + string(debug.Stack())) + } + }() + + f() + + return nil } diff --git a/pkg/integration/recording.go b/pkg/integration/recording.go deleted file mode 100644 index 3fa72aac1..000000000 --- a/pkg/integration/recording.go +++ /dev/null @@ -1,61 +0,0 @@ -package integration - -import ( - "encoding/json" - "io/ioutil" - "log" - "os" - "strconv" - - "github.com/jesseduffield/gocui" -) - -// this all relates to the old way of doing integration tests where you record yourself -// and then replay the events. - -func GetRecordingSpeed() float64 { - // humans are slow so this speeds things up. - speed := 1.0 - envReplaySpeed := os.Getenv("SPEED") - if envReplaySpeed != "" { - var err error - speed, err = strconv.ParseFloat(envReplaySpeed, 64) - if err != nil { - log.Fatal(err) - } - } - return speed -} - -func LoadRecording() (*gocui.Recording, error) { - path := os.Getenv("REPLAY_EVENTS_FROM") - - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - recording := &gocui.Recording{} - - err = json.Unmarshal(data, &recording) - if err != nil { - return nil, err - } - - return recording, nil -} - -func SaveRecording(recording *gocui.Recording) error { - if !RecordingEvents() { - return nil - } - - jsonEvents, err := json.Marshal(recording) - if err != nil { - return err - } - - path := recordEventsTo() - - return ioutil.WriteFile(path, jsonEvents, 0o600) -} diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 2e6ea34f6..ad4c942ea 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -1,7 +1,6 @@ package types import ( - "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -21,7 +20,7 @@ type Test interface { // so that they appear when lazygit runs SetupConfig(config *config.AppConfig) // this is called upon lazygit starting - Run(GuiAdapter) + Run(types.GuiAdapter) // e.g. '-debug' ExtraCmdArgs() string // for tests that are flakey and when we don't have time to fix them @@ -98,18 +97,3 @@ type Assert interface { // for when you just want to fail the test yourself Fail(errorMessage string) } - -type GuiAdapter interface { - PressKey(string) - Keys() config.KeybindingConfig - CurrentContext() types.Context - Model() *types.Model - Fail(message string) - // These two log methods are for the sake of debugging while testing. There's no need to actually - // commit any logging. - // logs to the normal place that you log to i.e. viewable with `lazygit --logs` - Log(message string) - // logs in the actual UI (in the commands panel) - LogUI(message string) - CheckedOutRef() *models.Branch -} diff --git a/test/runner/main.go b/test/runner/main.go index 691f0de4f..eef767564 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -5,7 +5,6 @@ import ( "log" "os" "os/exec" - "testing" "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" @@ -43,16 +42,16 @@ func main() { err := integration.RunTestsNew( log.Printf, runCmdInTerminal, - func(test types.Test, f func(*testing.T) error) { + func(test types.Test, f func() error) { if selectedTestName != "" && test.Name() != selectedTestName { return } - if err := f(nil); err != nil { + if err := f(); err != nil { log.Print(err.Error()) } }, mode, - func(_t *testing.T, expected string, actual string, prefix string) { //nolint:thelper + func(expected string, actual string, prefix string) { //nolint:thelper assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) }, includeSkipped, From ba96baee32f5d02173312b357327eb7478492f89 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 9 Aug 2022 21:27:12 +1000 Subject: [PATCH 05/16] move code from main into app package to allow test to be injected --- main.go | 22 +- pkg/app/app.go | 6 +- pkg/app/{run.go => entry_point.go} | 29 +- .../types/main_args.go => app/types/types.go} | 16 +- pkg/gui/gui.go | 32 +- .../{gui_adapter_impl.go => gui_adapter.go} | 21 +- pkg/gui/recent_repos_panel.go | 3 +- pkg/gui/test_mode.go | 8 +- pkg/gui/types/test.go | 27 - pkg/integration/README.md | 11 + .../integration.go} | 2 +- .../deprecated/integration_test.go} | 40 +- .../integration/deprecated/runner}/main.go | 10 +- .../integration/deprecated/tui}/main.go | 12 +- pkg/integration/env.go | 29 - pkg/integration/helpers/assert.go | 29 +- pkg/integration/helpers/input.go | 65 ++- pkg/integration/helpers/shell.go | 32 +- .../helpers/{test_impl.go => test.go} | 54 +- pkg/integration/integration.go | 509 +++++++++++++++--- .../integration_test.go} | 17 +- pkg/integration/integration_tests/tests.go | 19 - {test => pkg/integration}/runner/main.go | 18 +- .../branch/suggestions.go | 5 +- .../commit/commit.go | 5 +- .../commit/new_branch.go | 5 +- .../interactive_rebase/one.go | 5 +- pkg/integration/tests/tests.go | 18 + pkg/integration/tui/main.go | 323 +++++++++++ pkg/integration/types/types.go | 106 +--- .../commit/expected/repo/.git_keep/index | Bin 209 -> 209 bytes .../commit/expected/repo/.git_keep/logs/HEAD | 2 +- .../repo/.git_keep/logs/refs/heads/master | 2 +- .../46/0150760ff1f381c3f5769b919cb73107c5871a | Bin 125 -> 0 bytes .../94/4b9ea58bef8f6352c3a081a1d0037125bcaabc | Bin 0 -> 124 bytes .../expected/repo/.git_keep/refs/heads/master | 2 +- .../expected/repo/.git_keep/logs/HEAD | 8 +- .../repo/.git_keep/logs/refs/heads/master | 6 +- .../.git_keep/logs/refs/heads/my-branch-name | 2 +- .../0a/f36e404e6fec1c3a4d887e30622238e5ea0b2b | Bin 0 -> 147 bytes .../47/0038e1336649b2965305f9f6a82501a836810e | Bin 118 -> 0 bytes .../4e/72cd440eec154569568bff8d4c955052ae246c | Bin 0 -> 118 bytes .../56/3414ba32c967cfbe21a17fe892d6118c1c58e8 | 2 + .../62/a60693a2e154e745ee353f67a05156d0532c23 | Bin 148 -> 0 bytes .../c8/bec8f2b323cbb476e708bd10c145ea7cc9f726 | 2 - .../expected/repo/.git_keep/refs/heads/master | 2 +- .../repo/.git_keep/refs/heads/my-branch-name | 2 +- .../expected/repo/.git_keep/COMMIT_EDITMSG | 10 +- .../one/expected/repo/.git_keep/ORIG_HEAD | 2 +- .../one/expected/repo/.git_keep/REBASE_HEAD | 2 +- .../one/expected/repo/.git_keep/index | Bin 460 -> 460 bytes .../one/expected/repo/.git_keep/logs/HEAD | 20 +- .../repo/.git_keep/logs/refs/heads/master | 12 +- .../2e/2cd25ffdec58d32b5d549f8402bd054e22cc2a | 3 + .../67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d | 3 - .../84/b1ae9d83049341897c9388afffdc9049c3317f | 3 - .../8a/3839811a7a9f4c678090c9def892d1e7ad7e54 | Bin 0 -> 147 bytes .../90/fda12ce101e7d0d4594a879e5bbd1be3c857a8 | Bin 0 -> 147 bytes .../9c/68b57ac7b652fbebc5e93a9a1b72014400c269 | 2 - .../a1/a6f7bda6aeaa08ec75f590845780fde90d901c | Bin 117 -> 0 bytes .../aa/2585aff7d2278341ca816f187e623503d7c4fb | 4 - .../ab/a3469fd6fc584a6af9c0073873005ffaaea56c | 3 + .../b8/5535ebf12659044c33386376121d76756ceb59 | Bin 0 -> 222 bytes .../cb/7e56856ecee89fa44c613e094fcf962fe18cf1 | Bin 148 -> 0 bytes .../cc/9defb8ae9134f1a9a6c28a0006dc8c8cd78347 | Bin 0 -> 117 bytes .../da/71be1afbb03f46e91ab5de17d69f148bb009f3 | 4 + .../f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 | Bin 153 -> 0 bytes .../expected/repo/.git_keep/refs/heads/master | 2 +- 68 files changed, 1096 insertions(+), 482 deletions(-) rename pkg/app/{run.go => entry_point.go} (82%) rename pkg/{gui/types/main_args.go => app/types/types.go} (64%) rename pkg/gui/{gui_adapter_impl.go => gui_adapter.go} (68%) delete mode 100644 pkg/gui/types/test.go create mode 100644 pkg/integration/README.md rename pkg/integration/{integration_old.go => deprecated/integration.go} (99%) rename pkg/{gui/old_gui_test.go => integration/deprecated/integration_test.go} (79%) rename {test/runner_old => pkg/integration/deprecated/runner}/main.go (85%) rename {test/lazyintegration => pkg/integration/deprecated/tui}/main.go (97%) delete mode 100644 pkg/integration/env.go rename pkg/integration/helpers/{test_impl.go => test.go} (56%) rename pkg/{gui/gui_test.go => integration/integration_test.go} (73%) delete mode 100644 pkg/integration/integration_tests/tests.go rename {test => pkg/integration}/runner/main.go (69%) rename pkg/integration/{integration_tests => tests}/branch/suggestions.go (83%) rename pkg/integration/{integration_tests => tests}/commit/commit.go (78%) rename pkg/integration/{integration_tests => tests}/commit/new_branch.go (80%) rename pkg/integration/{integration_tests => tests}/interactive_rebase/one.go (84%) create mode 100644 pkg/integration/tests/tests.go create mode 100644 pkg/integration/tui/main.go delete mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a create mode 100644 test/integration_new/commit/commit/expected/repo/.git_keep/objects/94/4b9ea58bef8f6352c3a081a1d0037125bcaabc create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/0a/f36e404e6fec1c3a4d887e30622238e5ea0b2b delete mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/4e/72cd440eec154569568bff8d4c955052ae246c create mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/56/3414ba32c967cfbe21a17fe892d6118c1c58e8 delete mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/62/a60693a2e154e745ee353f67a05156d0532c23 delete mode 100644 test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/c8/bec8f2b323cbb476e708bd10c145ea7cc9f726 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/2e/2cd25ffdec58d32b5d549f8402bd054e22cc2a delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/8a/3839811a7a9f4c678090c9def892d1e7ad7e54 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/90/fda12ce101e7d0d4594a879e5bbd1be3c857a8 delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/ab/a3469fd6fc584a6af9c0073873005ffaaea56c create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/b8/5535ebf12659044c33386376121d76756ceb59 delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cb/7e56856ecee89fa44c613e094fcf962fe18cf1 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cc/9defb8ae9134f1a9a6c28a0006dc8c8cd78347 create mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/da/71be1afbb03f46e91ab5de17d69f148bb009f3 delete mode 100644 test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 diff --git a/main.go b/main.go index 8684b8cd6..6faf41ddf 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,8 @@ import ( "github.com/integrii/flaggy" "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/lazygit/pkg/integration" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" ) @@ -24,8 +26,9 @@ var ( func main() { cliArgs := parseCliArgsAndEnvVars() buildInfo := getBuildInfo() + integrationTest := getIntegrationTest() - app.Start(cliArgs, buildInfo, nil) + app.Start(cliArgs, buildInfo, integrationTest) } func parseCliArgsAndEnvVars() *app.CliArgs { @@ -129,3 +132,20 @@ func getBuildInfo() *app.BuildInfo { return buildInfo } + +func getIntegrationTest() integrationTypes.IntegrationTest { + integrationTestName := os.Getenv("LAZYGIT_TEST_NAME") + if integrationTestName == "" { + return nil + } + + // unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again + os.Unsetenv("LAZYGIT_TEST_NAME") + for _, candidateTest := range integration.Tests { + if candidateTest.Name() == integrationTestName { + return candidateTest + } + } + + panic("Could not find integration test with name: " + integrationTestName) +} diff --git a/pkg/app/app.go b/pkg/app/app.go index a45ffb118..3a1c127de 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -14,6 +14,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/generics/slices" + appTypes "github.com/jesseduffield/lazygit/pkg/app/types" "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/git_config" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" @@ -22,7 +23,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/constants" "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/gui" - "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/updates" ) @@ -41,7 +41,7 @@ type App struct { func Run( config config.AppConfigurer, common *common.Common, - startArgs types.StartArgs, + startArgs appTypes.StartArgs, ) { app, err := NewApp(config, common) @@ -217,7 +217,7 @@ func (app *App) setupRepo() (bool, error) { return false, nil } -func (app *App) Run(startArgs types.StartArgs) error { +func (app *App) Run(startArgs appTypes.StartArgs) error { err := app.Gui.RunAndHandleError(startArgs) return err } diff --git a/pkg/app/run.go b/pkg/app/entry_point.go similarity index 82% rename from pkg/app/run.go rename to pkg/app/entry_point.go index b1e2c396f..551b959a6 100644 --- a/pkg/app/run.go +++ b/pkg/app/entry_point.go @@ -10,9 +10,9 @@ import ( "strings" "github.com/jesseduffield/lazygit/pkg/app/daemon" + appTypes "github.com/jesseduffield/lazygit/pkg/app/types" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/env" - "github.com/jesseduffield/lazygit/pkg/gui/types" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/logs" "gopkg.in/yaml.v3" @@ -40,12 +40,7 @@ type BuildInfo struct { BuildSource string } -// only used when running integration tests -type TestConfig struct { - Test integrationTypes.Test -} - -func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { +func Start(cliArgs *CliArgs, buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTest) { if cliArgs.RepoPath != "" { if cliArgs.WorkTree != "" || cliArgs.GitDir != "" { log.Fatal("--path option is incompatible with the --work-tree and --git-dir options") @@ -118,8 +113,8 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { log.Fatal(err.Error()) } - if test != nil { - test.SetupConfig(appConfig) + if integrationTest != nil { + integrationTest.SetupConfig(appConfig) } common, err := NewCommon(appConfig) @@ -134,23 +129,23 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, test integrationTypes.Test) { parsedGitArg := parseGitArg(cliArgs.GitArg) - Run(appConfig, common, types.NewStartArgs(cliArgs.FilterPath, parsedGitArg, test)) + Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest)) } -func parseGitArg(gitArg string) types.GitArg { - typedArg := types.GitArg(gitArg) +func parseGitArg(gitArg string) appTypes.GitArg { + typedArg := appTypes.GitArg(gitArg) // using switch so that linter catches when a new git arg value is defined but not handled here switch typedArg { - case types.GitArgNone, types.GitArgStatus, types.GitArgBranch, types.GitArgLog, types.GitArgStash: + case appTypes.GitArgNone, appTypes.GitArgStatus, appTypes.GitArgBranch, appTypes.GitArgLog, appTypes.GitArgStash: return typedArg } permittedValues := []string{ - string(types.GitArgStatus), - string(types.GitArgBranch), - string(types.GitArgLog), - string(types.GitArgStash), + string(appTypes.GitArgStatus), + string(appTypes.GitArgBranch), + string(appTypes.GitArgLog), + string(appTypes.GitArgStash), } log.Fatalf("Invalid git arg value: '%s'. Must be one of the following values: %s. e.g. 'lazygit status'. See 'lazygit --help'.", diff --git a/pkg/gui/types/main_args.go b/pkg/app/types/types.go similarity index 64% rename from pkg/gui/types/main_args.go rename to pkg/app/types/types.go index 7d9b9fbb7..002111087 100644 --- a/pkg/gui/types/main_args.go +++ b/pkg/app/types/types.go @@ -1,4 +1,8 @@ -package types +package app + +import ( + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) // StartArgs is the struct that represents some things we want to do on program start type StartArgs struct { @@ -7,7 +11,7 @@ type StartArgs struct { // GitArg determines what context we open in GitArg GitArg // integration test (only relevant when invoking lazygit in the context of an integration test) - Test Test + IntegrationTest integrationTypes.IntegrationTest } type GitArg string @@ -20,10 +24,10 @@ const ( GitArgStash GitArg = "stash" ) -func NewStartArgs(filterPath string, gitArg GitArg, test Test) StartArgs { +func NewStartArgs(filterPath string, gitArg GitArg, test integrationTypes.IntegrationTest) StartArgs { return StartArgs{ - FilterPath: filterPath, - GitArg: gitArg, - Test: test, + FilterPath: filterPath, + GitArg: gitArg, + IntegrationTest: test, } } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 11c8af78b..8be5a4a4d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -10,6 +10,7 @@ import ( "time" "github.com/jesseduffield/gocui" + appTypes "github.com/jesseduffield/lazygit/pkg/app/types" "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/git_config" @@ -31,6 +32,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/tasks" "github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/updates" @@ -213,7 +215,7 @@ const ( COMPLETE ) -func (gui *Gui) onNewRepo(startArgs types.StartArgs, reuseState bool) error { +func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, reuseState bool) error { var err error gui.git, err = commands.NewGitCommand( gui.Common, @@ -245,7 +247,7 @@ func (gui *Gui) onNewRepo(startArgs types.StartArgs, reuseState bool) error { // it gets a bit confusing to land back in the status panel when visiting a repo // you've already switched from. There's no doubt some easy way to make the UX // optimal for all cases but I'm too lazy to think about what that is right now -func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) { +func (gui *Gui) resetState(startArgs appTypes.StartArgs, reuseState bool) { currentDir, err := os.Getwd() if reuseState { @@ -300,28 +302,28 @@ func (gui *Gui) resetState(startArgs types.StartArgs, reuseState bool) { gui.RepoStateMap[Repo(currentDir)] = gui.State } -func initialScreenMode(startArgs types.StartArgs) WindowMaximisation { - if startArgs.FilterPath != "" || startArgs.GitArg != types.GitArgNone { +func initialScreenMode(startArgs appTypes.StartArgs) WindowMaximisation { + if startArgs.FilterPath != "" || startArgs.GitArg != appTypes.GitArgNone { return SCREEN_HALF } else { return SCREEN_NORMAL } } -func initialContext(contextTree *context.ContextTree, startArgs types.StartArgs) types.IListContext { +func initialContext(contextTree *context.ContextTree, startArgs appTypes.StartArgs) types.IListContext { var initialContext types.IListContext = contextTree.Files if startArgs.FilterPath != "" { initialContext = contextTree.LocalCommits - } else if startArgs.GitArg != types.GitArgNone { + } else if startArgs.GitArg != appTypes.GitArgNone { switch startArgs.GitArg { - case types.GitArgStatus: + case appTypes.GitArgStatus: initialContext = contextTree.Files - case types.GitArgBranch: + case appTypes.GitArgBranch: initialContext = contextTree.Branches - case types.GitArgLog: + case appTypes.GitArgLog: initialContext = contextTree.LocalCommits - case types.GitArgStash: + case appTypes.GitArgStash: initialContext = contextTree.Stash default: panic("unhandled git arg") @@ -417,7 +419,7 @@ var RuneReplacements = map[rune]string{ graph.CommitSymbol: "o", } -func (gui *Gui) initGocui(headless bool, test types.Test) (*gocui.Gui, error) { +func (gui *Gui) initGocui(headless bool, test integrationTypes.IntegrationTest) (*gocui.Gui, error) { recordEvents := RecordingEvents() playMode := gocui.NORMAL if recordEvents { @@ -476,8 +478,8 @@ func (gui *Gui) viewTabMap() map[string][]context.TabView { } // Run: setup the gui with keybindings and start the mainloop -func (gui *Gui) Run(startArgs types.StartArgs) error { - g, err := gui.initGocui(Headless(), startArgs.Test) +func (gui *Gui) Run(startArgs appTypes.StartArgs) error { + g, err := gui.initGocui(Headless(), startArgs.IntegrationTest) if err != nil { return err } @@ -492,7 +494,7 @@ func (gui *Gui) Run(startArgs types.StartArgs) error { }) deadlock.Opts.Disable = !gui.Debug - gui.handleTestMode(startArgs.Test) + gui.handleTestMode(startArgs.IntegrationTest) gui.g.OnSearchEscape = gui.onSearchEscape if err := gui.Config.ReloadUserConfig(); err != nil { @@ -553,7 +555,7 @@ func (gui *Gui) Run(startArgs types.StartArgs) error { return gui.g.MainLoop() } -func (gui *Gui) RunAndHandleError(startArgs types.StartArgs) error { +func (gui *Gui) RunAndHandleError(startArgs appTypes.StartArgs) error { gui.stopChan = make(chan struct{}) return utils.SafeWithError(func() error { if err := gui.Run(startArgs); err != nil { diff --git a/pkg/gui/gui_adapter_impl.go b/pkg/gui/gui_adapter.go similarity index 68% rename from pkg/gui/gui_adapter_impl.go rename to pkg/gui/gui_adapter.go index 427b8eb47..5566f8b0f 100644 --- a/pkg/gui/gui_adapter_impl.go +++ b/pkg/gui/gui_adapter.go @@ -9,17 +9,18 @@ import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) // this gives our integration test a way of interacting with the gui for sending keypresses // and reading state. -type GuiAdapterImpl struct { +type GuiAdapter struct { gui *Gui } -var _ types.GuiAdapter = &GuiAdapterImpl{} +var _ integrationTypes.GuiAdapter = &GuiAdapter{} -func (self *GuiAdapterImpl) PressKey(keyStr string) { +func (self *GuiAdapter) PressKey(keyStr string) { key := keybindings.GetKey(keyStr) var r rune @@ -38,19 +39,19 @@ func (self *GuiAdapterImpl) PressKey(keyStr string) { ) } -func (self *GuiAdapterImpl) Keys() config.KeybindingConfig { +func (self *GuiAdapter) Keys() config.KeybindingConfig { return self.gui.Config.GetUserConfig().Keybinding } -func (self *GuiAdapterImpl) CurrentContext() types.Context { +func (self *GuiAdapter) CurrentContext() types.Context { return self.gui.c.CurrentContext() } -func (self *GuiAdapterImpl) Model() *types.Model { +func (self *GuiAdapter) Model() *types.Model { return self.gui.State.Model } -func (self *GuiAdapterImpl) Fail(message string) { +func (self *GuiAdapter) Fail(message string) { self.gui.g.Close() // need to give the gui time to close time.Sleep(time.Millisecond * 100) @@ -58,15 +59,15 @@ func (self *GuiAdapterImpl) Fail(message string) { } // logs to the normal place that you log to i.e. viewable with `lazygit --logs` -func (self *GuiAdapterImpl) Log(message string) { +func (self *GuiAdapter) Log(message string) { self.gui.c.Log.Warn(message) } // logs in the actual UI (in the commands panel) -func (self *GuiAdapterImpl) LogUI(message string) { +func (self *GuiAdapter) LogUI(message string) { self.gui.c.LogAction(message) } -func (self *GuiAdapterImpl) CheckedOutRef() *models.Branch { +func (self *GuiAdapter) CheckedOutRef() *models.Branch { return self.gui.helpers.Refs.GetCheckedOutRef() } diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index 73d6e54c5..705461726 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/jesseduffield/generics/slices" + appTypes "github.com/jesseduffield/lazygit/pkg/app/types" "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/env" "github.com/jesseduffield/lazygit/pkg/gui/presentation/icons" @@ -152,7 +153,7 @@ func (gui *Gui) dispatchSwitchToRepo(path string, reuse bool) error { gui.Mutexes.RefreshingFilesMutex.Lock() defer gui.Mutexes.RefreshingFilesMutex.Unlock() - return gui.onNewRepo(types.StartArgs{}, reuse) + return gui.onNewRepo(appTypes.StartArgs{}, reuse) } // updateRecentRepoList registers the fact that we opened lazygit in this repo, diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index 942e7824e..e9d596e80 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -9,20 +9,20 @@ import ( "time" "github.com/jesseduffield/gocui" - "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/utils" ) type IntegrationTest interface { - Run(guiAdapter *GuiAdapterImpl) + Run(guiAdapter *GuiAdapter) } -func (gui *Gui) handleTestMode(test types.Test) { +func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) { if test != nil { go func() { time.Sleep(time.Millisecond * 100) - test.Run(&GuiAdapterImpl{gui: gui}) + test.Run(&GuiAdapter{gui: gui}) gui.g.Update(func(*gocui.Gui) error { return gocui.ErrQuit diff --git a/pkg/gui/types/test.go b/pkg/gui/types/test.go deleted file mode 100644 index 55c1d50a8..000000000 --- a/pkg/gui/types/test.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import ( - "github.com/jesseduffield/lazygit/pkg/commands/models" - "github.com/jesseduffield/lazygit/pkg/config" -) - -type Test interface { - Run(GuiAdapter) - SetupConfig(config *config.AppConfig) -} - -// this is the interface through which our integration tests interact with the lazygit gui -type GuiAdapter interface { - PressKey(string) - Keys() config.KeybindingConfig - CurrentContext() Context - Model() *Model - Fail(message string) - // These two log methods are for the sake of debugging while testing. There's no need to actually - // commit any logging. - // logs to the normal place that you log to i.e. viewable with `lazygit --logs` - Log(message string) - // logs in the actual UI (in the commands panel) - LogUI(message string) - CheckedOutRef() *models.Branch -} diff --git a/pkg/integration/README.md b/pkg/integration/README.md new file mode 100644 index 000000000..55481ebe6 --- /dev/null +++ b/pkg/integration/README.md @@ -0,0 +1,11 @@ +# Integration Tests + +There's a lot happening in this package so it's worth a proper explanation. + +This package is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected. + +There are three ways to invoke a test: + +1. go run pkg/integration/runner/main.go commit/new_branch +2. go test pkg/integration/integration_test.go +3. diff --git a/pkg/integration/integration_old.go b/pkg/integration/deprecated/integration.go similarity index 99% rename from pkg/integration/integration_old.go rename to pkg/integration/deprecated/integration.go index 579892e3b..761c978e0 100644 --- a/pkg/integration/integration_old.go +++ b/pkg/integration/deprecated/integration.go @@ -1,4 +1,4 @@ -package integration +package deprecated import ( "encoding/json" diff --git a/pkg/gui/old_gui_test.go b/pkg/integration/deprecated/integration_test.go similarity index 79% rename from pkg/gui/old_gui_test.go rename to pkg/integration/deprecated/integration_test.go index 12e33432d..e66cc861a 100644 --- a/pkg/gui/old_gui_test.go +++ b/pkg/integration/deprecated/integration_test.go @@ -1,14 +1,18 @@ //go:build !windows // +build !windows -package gui +package deprecated import ( "fmt" + "io" + "io/ioutil" "os" + "os/exec" + "strconv" "testing" - "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/creack/pty" "github.com/stretchr/testify/assert" ) @@ -41,7 +45,7 @@ func TestOld(t *testing.T) { t.Skip("Skipping integration tests in short mode") } - mode := integration.GetModeFromEnv() + mode := GetModeFromEnv() speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" @@ -49,10 +53,10 @@ func TestOld(t *testing.T) { parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) testNumber := 0 - err := integration.RunTests( + err := RunTests( t.Logf, runCmdHeadless, - func(test *integration.Test, f func(*testing.T) error) { + func(test *Test, f func(*testing.T) error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return @@ -74,3 +78,29 @@ func TestOld(t *testing.T) { assert.NoError(t, err) } + +func tryConvert(numStr string, defaultVal int) int { + num, err := strconv.Atoi(numStr) + if err != nil { + return defaultVal + } + + return num +} + +func runCmdHeadless(cmd *exec.Cmd) error { + cmd.Env = append( + cmd.Env, + "HEADLESS=true", + "TERM=xterm", + ) + + f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 100, Cols: 100}) + if err != nil { + return err + } + + _, _ = io.Copy(ioutil.Discard, f) + + return f.Close() +} diff --git a/test/runner_old/main.go b/pkg/integration/deprecated/runner/main.go similarity index 85% rename from test/runner_old/main.go rename to pkg/integration/deprecated/runner/main.go index ea6af59e8..18398a450 100644 --- a/test/runner_old/main.go +++ b/pkg/integration/deprecated/runner/main.go @@ -7,11 +7,11 @@ import ( "os/exec" "testing" - "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/deprecated" "github.com/stretchr/testify/assert" ) -// Deprecated: This file is part of the old way of doing things. See test/runner_new/main.go for the new way +// Deprecated: This file is part of the old way of doing things. See pkg/integration/runner/main.go for the new way // see docs/Integration_Tests.md // This file can be invoked directly, but you might find it easier to go through @@ -22,15 +22,15 @@ import ( // as an env var. func main() { - mode := integration.GetModeFromEnv() + mode := deprecated.GetModeFromEnv() speedEnv := os.Getenv("SPEED") includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" selectedTestName := os.Args[1] - err := integration.RunTests( + err := deprecated.RunTests( log.Printf, runCmdInTerminal, - func(test *integration.Test, f func(*testing.T) error) { + func(test *deprecated.Test, f func(*testing.T) error) { if selectedTestName != "" && test.Name != selectedTestName { return } diff --git a/test/lazyintegration/main.go b/pkg/integration/deprecated/tui/main.go similarity index 97% rename from test/lazyintegration/main.go rename to pkg/integration/deprecated/tui/main.go index f62d92fbe..aa698dc8e 100644 --- a/test/lazyintegration/main.go +++ b/pkg/integration/deprecated/tui/main.go @@ -10,21 +10,23 @@ import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/style" - "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/deprecated" "github.com/jesseduffield/lazygit/pkg/secureexec" ) +// Deprecated. See lazy_integration for the new approach. + // this program lets you manage integration tests in a TUI. See docs/Integration_Tests.md for more info. type App struct { - tests []*integration.Test + tests []*deprecated.Test itemIdx int testDir string editing bool g *gocui.Gui } -func (app *App) getCurrentTest() *integration.Test { +func (app *App) getCurrentTest() *deprecated.Test { if len(app.tests) > 0 { return app.tests[app.itemIdx] } @@ -49,7 +51,7 @@ func (app *App) refreshTests() { } func (app *App) loadTests() { - tests, err := integration.LoadTests(app.testDir) + tests, err := deprecated.LoadTests(app.testDir) if err != nil { log.Panicln(err) } @@ -61,7 +63,7 @@ func (app *App) loadTests() { } func main() { - rootDir := integration.GetRootDirectory() + rootDir := deprecated.GetRootDirectory() testDir := filepath.Join(rootDir, "test", "integration") app := &App{testDir: testDir} diff --git a/pkg/integration/env.go b/pkg/integration/env.go deleted file mode 100644 index 89f25d85e..000000000 --- a/pkg/integration/env.go +++ /dev/null @@ -1,29 +0,0 @@ -package integration - -import ( - "os" - - "github.com/jesseduffield/generics/slices" - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" - "github.com/jesseduffield/lazygit/pkg/integration/types" -) - -// NEW integration test format stuff - -func IntegrationTestName() string { - return os.Getenv("LAZYGIT_TEST_NAME") -} - -func PlayingIntegrationTest() bool { - return IntegrationTestName() != "" -} - -func CurrentIntegrationTest() (types.Test, bool) { - if !PlayingIntegrationTest() { - return nil, false - } - - return slices.Find(integration_tests.Tests, func(test types.Test) bool { - return test.Name() == IntegrationTestName() - }) -} diff --git a/pkg/integration/helpers/assert.go b/pkg/integration/helpers/assert.go index 491bca348..41b280bcb 100644 --- a/pkg/integration/helpers/assert.go +++ b/pkg/integration/helpers/assert.go @@ -9,13 +9,17 @@ import ( integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) -type AssertImpl struct { - gui types.GuiAdapter +// through this struct we assert on the state of the lazygit gui + +type Assert struct { + gui integrationTypes.GuiAdapter } -var _ integrationTypes.Assert = &AssertImpl{} +func NewAssert(gui integrationTypes.GuiAdapter) *Assert { + return &Assert{gui: gui} +} -func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { +func (self *Assert) WorkingTreeFileCount(expectedCount int) { self.assertWithRetries(func() (bool, string) { actualCount := len(self.gui.Model().Files) @@ -26,7 +30,7 @@ func (self *AssertImpl) WorkingTreeFileCount(expectedCount int) { }) } -func (self *AssertImpl) CommitCount(expectedCount int) { +func (self *Assert) CommitCount(expectedCount int) { self.assertWithRetries(func() (bool, string) { actualCount := len(self.gui.Model().Commits) @@ -37,7 +41,7 @@ func (self *AssertImpl) CommitCount(expectedCount int) { }) } -func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { +func (self *Assert) HeadCommitMessage(expectedMessage string) { self.assertWithRetries(func() (bool, string) { if len(self.gui.Model().Commits) == 0 { return false, "Expected at least one commit to be present" @@ -55,21 +59,21 @@ func (self *AssertImpl) HeadCommitMessage(expectedMessage string) { }) } -func (self *AssertImpl) CurrentViewName(expectedViewName string) { +func (self *Assert) CurrentViewName(expectedViewName string) { self.assertWithRetries(func() (bool, string) { actual := self.gui.CurrentContext().GetView().Name() return actual == expectedViewName, fmt.Sprintf("Expected current view name to be '%s', but got '%s'", expectedViewName, actual) }) } -func (self *AssertImpl) CurrentBranchName(expectedViewName string) { +func (self *Assert) CurrentBranchName(expectedViewName string) { self.assertWithRetries(func() (bool, string) { actual := self.gui.CheckedOutRef().Name return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) }) } -func (self *AssertImpl) InListContext() { +func (self *Assert) InListContext() { self.assertWithRetries(func() (bool, string) { currentContext := self.gui.CurrentContext() _, ok := currentContext.(types.IListContext) @@ -77,14 +81,14 @@ func (self *AssertImpl) InListContext() { }) } -func (self *AssertImpl) SelectedLineContains(text string) { +func (self *Assert) SelectedLineContains(text string) { self.assertWithRetries(func() (bool, string) { line := self.gui.CurrentContext().GetView().SelectedLine() return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) }) } -func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { +func (self *Assert) assertWithRetries(test func() (bool, string)) { waitTimes := []int{0, 1, 5, 10, 200, 500, 1000} var message string @@ -101,6 +105,7 @@ func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { self.Fail(message) } -func (self *AssertImpl) Fail(message string) { +// for when you just want to fail the test yourself +func (self *Assert) Fail(message string) { self.gui.Fail(message) } diff --git a/pkg/integration/helpers/input.go b/pkg/integration/helpers/input.go index f23a08688..8c34fab5c 100644 --- a/pkg/integration/helpers/input.go +++ b/pkg/integration/helpers/input.go @@ -10,15 +10,15 @@ import ( integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) -type InputImpl struct { - gui types.GuiAdapter +type Impl struct { + gui integrationTypes.GuiAdapter keys config.KeybindingConfig - assert integrationTypes.Assert + assert *Assert pushKeyDelay int } -func NewInputImpl(gui types.GuiAdapter, keys config.KeybindingConfig, assert integrationTypes.Assert, pushKeyDelay int) *InputImpl { - return &InputImpl{ +func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Impl { + return &Impl{ gui: gui, keys: keys, assert: assert, @@ -26,93 +26,106 @@ func NewInputImpl(gui types.GuiAdapter, keys config.KeybindingConfig, assert int } } -var _ integrationTypes.Input = &InputImpl{} - -func (self *InputImpl) PressKeys(keyStrs ...string) { +// key is something like 'w' or ''. It's best not to pass a direct value, +// but instead to go through the default user config to get a more meaningful key name +func (self *Impl) PressKeys(keyStrs ...string) { for _, keyStr := range keyStrs { self.pressKey(keyStr) } } -func (self *InputImpl) pressKey(keyStr string) { +func (self *Impl) pressKey(keyStr string) { self.Wait(self.pushKeyDelay) self.gui.PressKey(keyStr) } -func (self *InputImpl) SwitchToStatusWindow() { +func (self *Impl) SwitchToStatusWindow() { self.pressKey(self.keys.Universal.JumpToBlock[0]) } -func (self *InputImpl) SwitchToFilesWindow() { +func (self *Impl) SwitchToFilesWindow() { self.pressKey(self.keys.Universal.JumpToBlock[1]) } -func (self *InputImpl) SwitchToBranchesWindow() { +func (self *Impl) SwitchToBranchesWindow() { self.pressKey(self.keys.Universal.JumpToBlock[2]) } -func (self *InputImpl) SwitchToCommitsWindow() { +func (self *Impl) SwitchToCommitsWindow() { self.pressKey(self.keys.Universal.JumpToBlock[3]) } -func (self *InputImpl) SwitchToStashWindow() { +func (self *Impl) SwitchToStashWindow() { self.pressKey(self.keys.Universal.JumpToBlock[4]) } -func (self *InputImpl) Type(content string) { +func (self *Impl) Type(content string) { for _, char := range content { self.pressKey(string(char)) } } -func (self *InputImpl) Confirm() { +// i.e. pressing enter +func (self *Impl) Confirm() { self.pressKey(self.keys.Universal.Confirm) } -func (self *InputImpl) Cancel() { +// i.e. pressing escape +func (self *Impl) Cancel() { self.pressKey(self.keys.Universal.Return) } -func (self *InputImpl) Select() { +// i.e. pressing space +func (self *Impl) Select() { self.pressKey(self.keys.Universal.Select) } -func (self *InputImpl) NextItem() { +// i.e. pressing down arrow +func (self *Impl) NextItem() { self.pressKey(self.keys.Universal.NextItem) } -func (self *InputImpl) PreviousItem() { +// i.e. pressing up arrow +func (self *Impl) PreviousItem() { self.pressKey(self.keys.Universal.PrevItem) } -func (self *InputImpl) ContinueMerge() { +func (self *Impl) ContinueMerge() { self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) self.assert.SelectedLineContains("continue") self.Confirm() } -func (self *InputImpl) ContinueRebase() { +func (self *Impl) ContinueRebase() { self.ContinueMerge() } -func (self *InputImpl) Wait(milliseconds int) { +// for when you want to allow lazygit to process something before continuing +func (self *Impl) Wait(milliseconds int) { time.Sleep(time.Duration(milliseconds) * time.Millisecond) } -func (self *InputImpl) LogUI(message string) { +func (self *Impl) LogUI(message string) { self.gui.LogUI(message) } -func (self *InputImpl) Log(message string) { +func (self *Impl) Log(message string) { self.gui.LogUI(message) } +// this will look for a list item in the current panel and if it finds it, it will +// enter the keypresses required to navigate to it. +// The test will fail if: +// - the user is not in a list item +// - no list item is found containing the given text +// - multiple list items are found containing the given text in the initial page of items +// // NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. // If this changes in future, we'll need to update this code to first attempt to find the item // in the current page and failing that, jump to the top of the view and iterate through all of it, // looking for the item. -func (self *InputImpl) NavigateToListItemContainingText(text string) { +func (self *Impl) NavigateToListItemContainingText(text string) { self.assert.InListContext() currentContext := self.gui.CurrentContext().(types.IListContext) diff --git a/pkg/integration/helpers/shell.go b/pkg/integration/helpers/shell.go index fdfb0d231..b70a8ffaa 100644 --- a/pkg/integration/helpers/shell.go +++ b/pkg/integration/helpers/shell.go @@ -5,16 +5,20 @@ import ( "io/ioutil" "os" - "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/secureexec" "github.com/mgutz/str" ) -type ShellImpl struct{} +// this is for running shell commands, mostly for the sake of setting up the repo +// but you can also run the commands from within lazygit to emulate things happening +// in the background. +type Shell struct{} -var _ types.Shell = &ShellImpl{} +func NewShell() *Shell { + return &Shell{} +} -func (s *ShellImpl) RunCommand(cmdStr string) types.Shell { +func (s *Shell) RunCommand(cmdStr string) *Shell { args := str.ToArgv(cmdStr) cmd := secureexec.Command(args[0], args[1:]...) cmd.Env = os.Environ() @@ -27,7 +31,7 @@ func (s *ShellImpl) RunCommand(cmdStr string) types.Shell { return s } -func (s *ShellImpl) CreateFile(path string, content string) types.Shell { +func (s *Shell) CreateFile(path string, content string) *Shell { err := ioutil.WriteFile(path, []byte(content), 0o644) if err != nil { panic(fmt.Sprintf("error creating file: %s\n%s", path, err)) @@ -36,33 +40,37 @@ func (s *ShellImpl) CreateFile(path string, content string) types.Shell { return s } -func (s *ShellImpl) NewBranch(name string) types.Shell { +func (s *Shell) NewBranch(name string) *Shell { return s.RunCommand("git checkout -b " + name) } -func (s *ShellImpl) GitAdd(path string) types.Shell { +func (s *Shell) GitAdd(path string) *Shell { return s.RunCommand(fmt.Sprintf("git add \"%s\"", path)) } -func (s *ShellImpl) GitAddAll() types.Shell { +func (s *Shell) GitAddAll() *Shell { return s.RunCommand("git add -A") } -func (s *ShellImpl) Commit(message string) types.Shell { +func (s *Shell) Commit(message string) *Shell { return s.RunCommand(fmt.Sprintf("git commit -m \"%s\"", message)) } -func (s *ShellImpl) EmptyCommit(message string) types.Shell { +func (s *Shell) EmptyCommit(message string) *Shell { return s.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message)) } -func (s *ShellImpl) CreateFileAndAdd(fileName string, fileContents string) types.Shell { +// convenience method for creating a file and adding it +func (s *Shell) CreateFileAndAdd(fileName string, fileContents string) *Shell { return s. CreateFile(fileName, fileContents). GitAdd(fileName) } -func (s *ShellImpl) CreateNCommits(n int) types.Shell { +// creates commits 01, 02, 03, ..., n with a new file in each +// The reason for padding with zeroes is so that it's easier to do string +// matches on the commit messages when there are many of them +func (s *Shell) CreateNCommits(n int) *Shell { for i := 1; i <= n; i++ { s.CreateFileAndAdd( fmt.Sprintf("file%02d.txt", i), diff --git a/pkg/integration/helpers/test_impl.go b/pkg/integration/helpers/test.go similarity index 56% rename from pkg/integration/helpers/test_impl.go rename to pkg/integration/helpers/test.go index eaec83561..6e5c45886 100644 --- a/pkg/integration/helpers/test_impl.go +++ b/pkg/integration/helpers/test.go @@ -6,37 +6,40 @@ import ( "strings" "github.com/jesseduffield/lazygit/pkg/config" - guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/integration/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/utils" ) -type TestImpl struct { +// Test describes an integration tests that will be run against the lazygit gui. + +type Test struct { name string description string extraCmdArgs string skip bool - setupRepo func(shell types.Shell) + setupRepo func(shell *Shell) setupConfig func(config *config.AppConfig) run func( - shell types.Shell, - input types.Input, - assert types.Assert, + shell *Shell, + input *Impl, + assert *Assert, keys config.KeybindingConfig, ) } +var _ integrationTypes.IntegrationTest = &Test{} + type NewTestArgs struct { Description string - SetupRepo func(shell types.Shell) + SetupRepo func(shell *Shell) SetupConfig func(config *config.AppConfig) - Run func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) + Run func(shell *Shell, input *Impl, assert *Assert, keys config.KeybindingConfig) ExtraCmdArgs string Skip bool } -func NewTest(args NewTestArgs) *TestImpl { - return &TestImpl{ +func NewTest(args NewTestArgs) *Test { + return &Test{ name: testNameFromFilePath(), description: args.Description, extraCmdArgs: args.ExtraCmdArgs, @@ -47,45 +50,48 @@ func NewTest(args NewTestArgs) *TestImpl { } } -var _ types.Test = (*TestImpl)(nil) - -func (self *TestImpl) Name() string { +func (self *Test) Name() string { return self.name } -func (self *TestImpl) Description() string { +func (self *Test) Description() string { return self.description } -func (self *TestImpl) ExtraCmdArgs() string { +func (self *Test) ExtraCmdArgs() string { return self.extraCmdArgs } -func (self *TestImpl) Skip() bool { +func (self *Test) Skip() bool { return self.skip } -func (self *TestImpl) SetupConfig(config *config.AppConfig) { +func (self *Test) SetupConfig(config *config.AppConfig) { self.setupConfig(config) } -func (self *TestImpl) SetupRepo(shell types.Shell) { +func (self *Test) SetupRepo(shell *Shell) { self.setupRepo(shell) } // I want access to all contexts, the model, the ability to press a key, the ability to log, -func (self *TestImpl) Run(gui guiTypes.GuiAdapter) { - shell := &ShellImpl{} - assert := &AssertImpl{gui: gui} +func (self *Test) Run(gui integrationTypes.GuiAdapter) { + shell := NewShell() + assert := NewAssert(gui) keys := gui.Keys() - input := NewInputImpl(gui, keys, assert, KeyPressDelay()) + input := NewInput(gui, keys, assert, KeyPressDelay()) self.run(shell, input, assert, keys) + + if KeyPressDelay() > 0 { + // the dev would want to see the final state if they're running in slow mode + input.Wait(2000) + } } func testNameFromFilePath() string { path := utils.FilePath(3) - name := strings.Split(path, "integration/integration_tests/")[1] + name := strings.Split(path, "integration/tests/")[1] return name[:len(name)-len(".go")] } diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index de440c4ea..cb2b79861 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -4,26 +4,49 @@ import ( "errors" "fmt" "io/ioutil" + "log" "os" "os/exec" "path/filepath" - "runtime/debug" + "strings" - "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/integration/helpers" - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" - "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/jesseduffield/lazygit/pkg/integration/tests" + "github.com/stretchr/testify/assert" ) // this is the integration runner for the new and improved integration interface +var Tests = tests.Tests + +type Mode int + +const ( + // Default: if a snapshot test fails, the we'll be asked whether we want to update it + ASK_TO_UPDATE_SNAPSHOT = iota + // fails the test if the snapshots don't match + CHECK_SNAPSHOT + // runs the test and updates the snapshot + UPDATE_SNAPSHOT + // This just makes use of the setup step of the test to get you into + // a lazygit session. Then you'll be able to do whatever you want. Useful + // when you want to test certain things without needing to manually set + // up the situation yourself. + // fails the test if the snapshots don't match + SANDBOX +) + +type ( + logf func(format string, formatArgs ...interface{}) +) + func RunTestsNew( - logf func(format string, formatArgs ...interface{}), + logf logf, runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test types.Test, f func() error), + fnWrapper func(test *helpers.Test, f func() error), mode Mode, - onFail func(expected string, actual string, prefix string), includeSkipped bool, ) error { rootDir := GetRootDirectory() @@ -40,7 +63,7 @@ func RunTestsNew( return err } - for _, test := range integration_tests.Tests { + for _, test := range Tests { test := test fnWrapper(test, func() error { //nolint: thelper @@ -66,62 +89,55 @@ func RunTestsNew( configDir := filepath.Join(testPath, "used_config") - err = runLazygit(test, testPath, rootDir) + cmd, err := getLazygitCommandNew(test, testPath, rootDir) if err != nil { return err } - if mode == UPDATE_SNAPSHOT { - // create/update snapshot - err = oscommands.CopyDir(actualDir, expectedDir) - if err != nil { - return err - } - - if err := renameSpecialPaths(expectedDir); err != nil { - return err - } - - logf("%s", "updated snapshot") - } else { - if err := validateSameRepos(expectedDir, actualDir); err != nil { - return err - } - - // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir - expectedFiles, err := ioutil.ReadDir(expectedDir) - if err != nil { - return err - } - - for _, f := range expectedFiles { - if !f.IsDir() { - return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") - } - - // get corresponding file name from actual dir - actualRepoPath := filepath.Join(actualDir, f.Name()) - expectedRepoPath := filepath.Join(expectedDir, f.Name()) - - actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) - if err != nil { - return err - } - - if expectedRepo != actualRepo { - // get the log file and print it - bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) - if err != nil { - return err - } - logf("%s", string(bytes)) - - onFail(expectedRepo, actualRepo, f.Name()) - } - } + err = runCmd(cmd) + if err != nil { + return err } - logf("test passed: %s", test.Name()) + switch mode { + case UPDATE_SNAPSHOT: + if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + return err + } + logf("Test passed: %s", test.Name()) + case CHECK_SNAPSHOT: + if err := compareSnapshots(logf, configDir, actualDir, expectedDir, test.Name()); err != nil { + return err + } + logf("Test passed: %s", test.Name()) + case ASK_TO_UPDATE_SNAPSHOT: + if _, err := os.Stat(expectedDir); os.IsNotExist(err) { + if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + return err + } + logf("No existing snapshot found for %s. Created snapshot.", test.Name()) + + return nil + } + + if err := compareSnapshots(logf, configDir, actualDir, expectedDir, test.Name()); err != nil { + logf("%s", err) + + // prompt user whether to update the snapshot (Y/N) + if promptUserToUpdateSnapshot() { + if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + return err + } + logf("Snapshot updated: %s", test.Name()) + } else { + return err + } + } + + logf("Test passed: %s", test.Name()) + case SANDBOX: + logf("Session exited") + } return nil }) @@ -130,12 +146,95 @@ func RunTestsNew( return nil } -func createFixtureNew(test types.Test, actualDir string, rootDir string) error { +func promptUserToUpdateSnapshot() bool { + fmt.Println("Test failed. Update snapshot? (y/n)") + var input string + fmt.Scanln(&input) + return input == "y" +} + +func updateSnapshot(logf logf, actualDir string, expectedDir string) error { + // create/update snapshot + err := oscommands.CopyDir(actualDir, expectedDir) + if err != nil { + return err + } + + if err := renameSpecialPaths(expectedDir); err != nil { + return err + } + + return err +} + +func compareSnapshots(logf logf, configDir string, actualDir string, expectedDir string, testName string) error { + // there are a couple of reasons we're not generating the snapshot in expectedDir directly: + // Firstly we don't want to have to revert our .git file back to .git_keep. + // Secondly, the act of calling git commands like 'git status' actually changes the index + // for some reason, and we don't want to leave your lazygit working tree dirty as a result. + expectedDirCopy := filepath.Join(os.TempDir(), "expected_dir_test", testName) + err := oscommands.CopyDir(expectedDir, expectedDirCopy) + if err != nil { + return err + } + + defer func() { + err := os.RemoveAll(expectedDirCopy) + if err != nil { + panic(err) + } + }() + + if err := restoreSpecialPaths(expectedDirCopy); err != nil { + return err + } + + err = validateSameRepos(expectedDirCopy, actualDir) + if err != nil { + return err + } + + // iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDirCopy) + if err != nil { + return err + } + + for _, f := range expectedFiles { + if !f.IsDir() { + return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory") + } + + // get corresponding file name from actual dir + actualRepoPath := filepath.Join(actualDir, f.Name()) + expectedRepoPath := filepath.Join(expectedDirCopy, f.Name()) + + actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath) + if err != nil { + return err + } + + if expectedRepo != actualRepo { + // get the log file and print it + bytes, err := ioutil.ReadFile(filepath.Join(configDir, "development.log")) + if err != nil { + return err + } + logf("%s", string(bytes)) + + return errors.New(getDiff(f.Name(), actualRepo, expectedRepo)) + } + } + + return nil +} + +func createFixtureNew(test *helpers.Test, actualDir string, rootDir string) error { if err := os.Chdir(actualDir); err != nil { panic(err) } - shell := &helpers.ShellImpl{} + shell := helpers.NewShell() shell.RunCommand("git init") shell.RunCommand(`git config user.email "CI@example.com"`) shell.RunCommand(`git config user.name "CI"`) @@ -150,7 +249,9 @@ func createFixtureNew(test types.Test, actualDir string, rootDir string) error { return nil } -func runLazygit(test types.Test, testPath string, rootDir string) error { +func getLazygitCommandNew(test *helpers.Test, testPath string, rootDir string) (*exec.Cmd, error) { + osCommand := oscommands.NewDummyOSCommand() + templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") actualRepoDir := filepath.Join(testPath, "actual", "repo") @@ -158,38 +259,278 @@ func runLazygit(test types.Test, testPath string, rootDir string) error { err := os.RemoveAll(configDir) if err != nil { - return err + return nil, err } err = oscommands.CopyDir(templateConfigDir, configDir) + if err != nil { + return nil, err + } + + cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, test.ExtraCmdArgs()) + + cmdObj := osCommand.Cmd.New(cmdStr) + + cmdObj.AddEnvVars(fmt.Sprintf("LAZYGIT_TEST_NAME=%s", test.Name())) + + return cmdObj.GetCmd(), nil +} + +func GetModeFromEnv() Mode { + switch os.Getenv("MODE") { + case "", "ask": + return ASK_TO_UPDATE_SNAPSHOT + case "check": + return CHECK_SNAPSHOT + case "updateSnapshot": + return UPDATE_SNAPSHOT + case "sandbox": + return SANDBOX + default: + log.Fatalf("unknown test mode: %s, must be one of [test, record, updateSnapshot, sandbox]", os.Getenv("MODE")) + panic("unreachable") + } +} + +func GetRootDirectory() string { + path, err := os.Getwd() + if err != nil { + panic(err) + } + + for { + _, err := os.Stat(filepath.Join(path, ".git")) + + if err == nil { + return path + } + + if !os.IsNotExist(err) { + panic(err) + } + + path = filepath.Dir(path) + + if path == "/" { + log.Fatal("must run in lazygit folder or child folder") + } + } +} + +func tempLazygitPath() string { + return filepath.Join("/tmp", "lazygit", "test_lazygit") +} + +func generateSnapshots(actualDir string, expectedDir string) (string, string, error) { + actual, err := generateSnapshot(actualDir) + if err != nil { + return "", "", err + } + + expected, err := generateSnapshot(expectedDir) + if err != nil { + return "", "", err + } + + return actual, expected, nil +} + +// note that we don't actually store this snapshot in the lazygit repo. +// Instead we store the whole expected git repo of our test, so that +// we can easily change what we want to compare without needing to regenerate +// snapshots for each test. +func generateSnapshot(dir string) (string, error) { + osCommand := oscommands.NewDummyOSCommand() + + _, err := os.Stat(filepath.Join(dir, ".git")) + if err != nil { + return "git directory not found", nil + } + + snapshot := "" + + cmdStrs := []string{ + `remote show -n origin`, // remote branches + // TODO: find a way to bring this back without breaking tests + // `ls-remote origin`, + `status`, // file tree + `log --pretty=%B|%an|%ae -p -1`, // log + `tag -n`, // tags + `stash list`, // stash + `submodule foreach 'git status'`, // submodule status + `submodule foreach 'git log --pretty=%B -p -1'`, // submodule log + `submodule foreach 'git tag -n'`, // submodule tags + `submodule foreach 'git stash list'`, // submodule stash + } + + for _, cmdStr := range cmdStrs { + // ignoring error for now. If there's an error it could be that there are no results + output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput() + + snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output) + } + + snapshot += "files in repo:\n" + err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.IsDir() { + if f.Name() == ".git" { + return filepath.SkipDir + } + return nil + } + + bytes, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + relativePath, err := filepath.Rel(dir, path) + if err != nil { + return err + } + snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes)) + + return nil + }) + + if err != nil { + return "", err + } + + return snapshot, nil +} + +func getPathsToRename(dir string, needle string, contains string) []string { + pathsToRename := []string{} + + err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) { + pathsToRename = append(pathsToRename, path) + } + + return nil + }) + if err != nil { + panic(err) + } + + return pathsToRename +} + +var specialPathMappings = []struct{ original, new, contains string }{ + // git refuses to track .git or .gitmodules in subdirectories so we need to rename them + {".git", ".git_keep", ""}, + {".gitmodules", ".gitmodules_keep", ""}, + // we also need git to ignore the contents of our test gitignore files so that + // we actually commit files that are ignored within the test. + {".gitignore", "lg_ignore_file", ""}, + // this is the .git/info/exclude file. We're being a little more specific here + // so that we don't accidentally mess with some other file named 'exclude' in the test. + {"exclude", "lg_exclude_file", ".git/info/exclude"}, +} + +func renameSpecialPaths(dir string) error { + for _, specialPath := range specialPathMappings { + for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) { + err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new)) + if err != nil { + return err + } + } + } + + return nil +} + +func restoreSpecialPaths(dir string) error { + for _, specialPath := range specialPathMappings { + for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) { + err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original)) + if err != nil { + return err + } + } + } + + return nil +} + +// validates that the actual and expected dirs have the same repo names (doesn't actually check the contents of the repos) +func validateSameRepos(expectedDir string, actualDir string) error { + // iterate through each repo in the expected dir and compare to the corresponding repo in the actual dir + expectedFiles, err := ioutil.ReadDir(expectedDir) if err != nil { return err } - // TODO: support test.ExtraCmdArgs in some form. - cliArgs := &app.CliArgs{ - Debug: true, - UseConfigDir: configDir, - RepoPath: actualRepoDir, + var actualFiles []os.FileInfo + actualFiles, err = ioutil.ReadDir(actualDir) + if err != nil { + return err } - buildInfo := &app.BuildInfo{ - Commit: "1234abc", - Date: "2020-01-01", - Version: "1.0.0", - BuildSource: "unknown", + expectedFileNames := slices.Map(expectedFiles, getFileName) + actualFileNames := slices.Map(actualFiles, getFileName) + if !slices.Equal(expectedFileNames, actualFileNames) { + return fmt.Errorf("expected and actual repo dirs do not match: expected: %s, actual: %s", expectedFileNames, actualFileNames) } - return convertPanicToError(func() { app.Start(cliArgs, buildInfo, test) }) -} - -func convertPanicToError(f func()) (err error) { //nolint: nakedret - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("Lazygit panicked. Stacktrace:: \n" + string(debug.Stack())) - } - }() - - f() - return nil } + +func getFileName(f os.FileInfo) string { + return f.Name() +} + +func findOrCreateDir(path string) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(path, 0o777) + if err != nil { + panic(err) + } + } else { + panic(err) + } + } +} + +func prepareIntegrationTestDir(actualDir string) { + // remove contents of integration test directory + dir, err := ioutil.ReadDir(actualDir) + if err != nil { + if os.IsNotExist(err) { + err = os.Mkdir(actualDir, 0o777) + if err != nil { + panic(err) + } + } else { + panic(err) + } + } + for _, d := range dir { + os.RemoveAll(filepath.Join(actualDir, d.Name())) + } +} + +func getDiff(prefix string, expected string, actual string) string { + mockT := &MockTestingT{} + assert.Equal(mockT, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) + return mockT.err +} + +type MockTestingT struct { + err string +} + +func (self *MockTestingT) Errorf(format string, args ...interface{}) { + self.err += fmt.Sprintf(format, args...) +} diff --git a/pkg/gui/gui_test.go b/pkg/integration/integration_test.go similarity index 73% rename from pkg/gui/gui_test.go rename to pkg/integration/integration_test.go index 2d698d34c..3e9da8fe9 100644 --- a/pkg/gui/gui_test.go +++ b/pkg/integration/integration_test.go @@ -1,13 +1,12 @@ //go:build !windows // +build !windows -package gui +package integration // this is the new way of running tests. See pkg/integration/integration_tests/commit.go // for an example import ( - "fmt" "io" "io/ioutil" "os" @@ -16,27 +15,26 @@ import ( "testing" "github.com/creack/pty" - "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/stretchr/testify/assert" ) -func Test(t *testing.T) { +func TestIntegration(t *testing.T) { if testing.Short() { t.Skip("Skipping integration tests in short mode") } - mode := integration.GetModeFromEnv() + mode := GetModeFromEnv() includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1) parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) testNumber := 0 - err := integration.RunTestsNew( + err := RunTestsNew( t.Logf, runCmdHeadless, - func(test types.Test, f func() error) { + func(test *helpers.Test, f func() error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return @@ -48,9 +46,6 @@ func Test(t *testing.T) { }) }, mode, - func(expected string, actual string, prefix string) { - assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) - }, includeSkipped, ) diff --git a/pkg/integration/integration_tests/tests.go b/pkg/integration/integration_tests/tests.go deleted file mode 100644 index 752929698..000000000 --- a/pkg/integration/integration_tests/tests.go +++ /dev/null @@ -1,19 +0,0 @@ -package integration_tests - -import ( - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/branch" - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/commit" - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/interactive_rebase" - - "github.com/jesseduffield/lazygit/pkg/integration/types" -) - -// Here is where we lists the actual tests that will run. When you create a new test, -// be sure to add it to this list. - -var Tests = []types.Test{ - commit.Commit, - commit.NewBranch, - branch.Suggestions, - interactive_rebase.One, -} diff --git a/test/runner/main.go b/pkg/integration/runner/main.go similarity index 69% rename from test/runner/main.go rename to pkg/integration/runner/main.go index eef767564..07d1bf28f 100644 --- a/test/runner/main.go +++ b/pkg/integration/runner/main.go @@ -1,15 +1,12 @@ package main import ( - "fmt" "log" "os" "os/exec" "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/integration/integration_tests" - "github.com/jesseduffield/lazygit/pkg/integration/types" - "github.com/stretchr/testify/assert" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) // see docs/Integration_Tests.md @@ -28,7 +25,7 @@ func main() { // check if our given test name actually exists if selectedTestName != "" { found := false - for _, test := range integration_tests.Tests { + for _, test := range integration.Tests { if test.Name() == selectedTestName { found = true break @@ -42,7 +39,7 @@ func main() { err := integration.RunTestsNew( log.Printf, runCmdInTerminal, - func(test types.Test, f func() error) { + func(test *helpers.Test, f func() error) { if selectedTestName != "" && test.Name() != selectedTestName { return } @@ -51,9 +48,6 @@ func main() { } }, mode, - func(expected string, actual string, prefix string) { //nolint:thelper - assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual)) - }, includeSkipped, ) if err != nil { @@ -61,12 +55,6 @@ func main() { } } -type MockTestingT struct{} - -func (t MockTestingT) Errorf(format string, args ...interface{}) { - fmt.Printf(format, args...) -} - func runCmdInTerminal(cmd *exec.Cmd) error { cmd.Stdout = os.Stdout cmd.Stdin = os.Stdin diff --git a/pkg/integration/integration_tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go similarity index 83% rename from pkg/integration/integration_tests/branch/suggestions.go rename to pkg/integration/tests/branch/suggestions.go index a925280e7..edceb5e8e 100644 --- a/pkg/integration/integration_tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -3,7 +3,6 @@ package branch import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/integration/helpers" - "github.com/jesseduffield/lazygit/pkg/integration/types" ) var Suggestions = helpers.NewTest(helpers.NewTestArgs{ @@ -11,7 +10,7 @@ var Suggestions = helpers.NewTest(helpers.NewTestArgs{ ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell types.Shell) { + SetupRepo: func(shell *helpers.Shell) { shell. EmptyCommit("my commit message"). NewBranch("new-branch"). @@ -21,7 +20,7 @@ var Suggestions = helpers.NewTest(helpers.NewTestArgs{ NewBranch("other-new-branch-2"). NewBranch("other-new-branch-3") }, - Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() input.PressKeys(keys.Branches.CheckoutBranchByName) diff --git a/pkg/integration/integration_tests/commit/commit.go b/pkg/integration/tests/commit/commit.go similarity index 78% rename from pkg/integration/integration_tests/commit/commit.go rename to pkg/integration/tests/commit/commit.go index 9fd3bb356..932d9cf6f 100644 --- a/pkg/integration/integration_tests/commit/commit.go +++ b/pkg/integration/tests/commit/commit.go @@ -3,7 +3,6 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/integration/helpers" - "github.com/jesseduffield/lazygit/pkg/integration/types" ) var Commit = helpers.NewTest(helpers.NewTestArgs{ @@ -11,11 +10,11 @@ var Commit = helpers.NewTest(helpers.NewTestArgs{ ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell types.Shell) { + SetupRepo: func(shell *helpers.Shell) { shell.CreateFile("myfile", "myfile content") shell.CreateFile("myfile2", "myfile2 content") }, - Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { assert.CommitCount(0) input.Select() diff --git a/pkg/integration/integration_tests/commit/new_branch.go b/pkg/integration/tests/commit/new_branch.go similarity index 80% rename from pkg/integration/integration_tests/commit/new_branch.go rename to pkg/integration/tests/commit/new_branch.go index 9669937fa..e1e5090e8 100644 --- a/pkg/integration/integration_tests/commit/new_branch.go +++ b/pkg/integration/tests/commit/new_branch.go @@ -3,7 +3,6 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/integration/helpers" - "github.com/jesseduffield/lazygit/pkg/integration/types" ) var NewBranch = helpers.NewTest(helpers.NewTestArgs{ @@ -11,13 +10,13 @@ var NewBranch = helpers.NewTest(helpers.NewTestArgs{ ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell types.Shell) { + SetupRepo: func(shell *helpers.Shell) { shell. EmptyCommit("commit 1"). EmptyCommit("commit 2"). EmptyCommit("commit 3") }, - Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { assert.CommitCount(3) input.SwitchToCommitsWindow() diff --git a/pkg/integration/integration_tests/interactive_rebase/one.go b/pkg/integration/tests/interactive_rebase/one.go similarity index 84% rename from pkg/integration/integration_tests/interactive_rebase/one.go rename to pkg/integration/tests/interactive_rebase/one.go index d8899569c..d901b9c47 100644 --- a/pkg/integration/integration_tests/interactive_rebase/one.go +++ b/pkg/integration/tests/interactive_rebase/one.go @@ -3,7 +3,6 @@ package interactive_rebase import ( "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/integration/helpers" - "github.com/jesseduffield/lazygit/pkg/integration/types" ) var One = helpers.NewTest(helpers.NewTestArgs{ @@ -11,11 +10,11 @@ var One = helpers.NewTest(helpers.NewTestArgs{ ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell types.Shell) { + SetupRepo: func(shell *helpers.Shell) { shell. CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 }, - Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { input.SwitchToCommitsWindow() assert.CurrentViewName("commits") diff --git a/pkg/integration/tests/tests.go b/pkg/integration/tests/tests.go new file mode 100644 index 000000000..c2e360e43 --- /dev/null +++ b/pkg/integration/tests/tests.go @@ -0,0 +1,18 @@ +package tests + +import ( + "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/tests/branch" + "github.com/jesseduffield/lazygit/pkg/integration/tests/commit" + "github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase" +) + +// Here is where we lists the actual tests that will run. When you create a new test, +// be sure to add it to this list. + +var Tests = []*helpers.Test{ + commit.Commit, + commit.NewBranch, + branch.Suggestions, + interactive_rebase.One, +} diff --git a/pkg/integration/tui/main.go b/pkg/integration/tui/main.go new file mode 100644 index 000000000..023c764b5 --- /dev/null +++ b/pkg/integration/tui/main.go @@ -0,0 +1,323 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui" + "github.com/jesseduffield/lazygit/pkg/gui/style" + "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/secureexec" +) + +// this program lets you manage integration tests in a TUI. See docs/Integration_Tests.md for more info. + +type App struct { + tests []*helpers.Test + itemIdx int + testDir string + filtering bool + g *gocui.Gui +} + +func (app *App) getCurrentTest() *helpers.Test { + if len(app.tests) > 0 { + return app.tests[app.itemIdx] + } + return nil +} + +func (app *App) refreshTests() { + app.loadTests() + app.g.Update(func(*gocui.Gui) error { + listView, err := app.g.View("list") + if err != nil { + return err + } + + listView.Clear() + for _, test := range app.tests { + fmt.Fprintln(listView, test.Name()) + } + + return nil + }) +} + +func (app *App) loadTests() { + app.tests = integration.Tests + if app.itemIdx > len(app.tests)-1 { + app.itemIdx = len(app.tests) - 1 + } +} + +func main() { + rootDir := integration.GetRootDirectory() + testDir := filepath.Join(rootDir, "test", "integration") + + app := &App{testDir: testDir} + app.loadTests() + + g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements) + if err != nil { + log.Panicln(err) + } + + g.Cursor = false + + app.g = g + + g.SetManagerFunc(app.layout) + + if err := g.SetKeybinding("list", gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if app.itemIdx > 0 { + app.itemIdx-- + } + listView, err := g.View("list") + if err != nil { + return err + } + listView.FocusPoint(0, app.itemIdx) + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", 'q', gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", 's', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/runner/main.go %s", currentTest.Name())) + app.runSubprocess(cmd) + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/runner/main.go %s", currentTest.Name())) + app.runSubprocess(cmd) + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", 't', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true KEY_PRESS_DELAY=200 go run pkg/integration/runner/main.go %s", currentTest.Name())) + app.runSubprocess(cmd) + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", 'o', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r pkg/integration/tests/%s", currentTest.Name())) + if err := cmd.Run(); err != nil { + return err + } + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", 'O', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code test/integration_new/%s", currentTest.Name())) + if err := cmd.Run(); err != nil { + return err + } + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("list", '/', gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + app.filtering = true + if _, err := g.SetCurrentView("editor"); err != nil { + return err + } + editorView, err := g.View("editor") + if err != nil { + return err + } + editorView.Clear() + + return nil + }); err != nil { + log.Panicln(err) + } + + // not using the editor yet, but will use it to help filter the list + if err := g.SetKeybinding("editor", gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + app.filtering = false + if _, err := g.SetCurrentView("list"); err != nil { + return err + } + + return nil + }); err != nil { + log.Panicln(err) + } + + err = g.MainLoop() + g.Close() + switch err { + case gocui.ErrQuit: + return + default: + log.Panicln(err) + } +} + +func (app *App) runSubprocess(cmd *exec.Cmd) { + if err := gocui.Screen.Suspend(); err != nil { + panic(err) + } + + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + log.Println(err.Error()) + } + cmd.Stdin = nil + cmd.Stderr = nil + cmd.Stdout = nil + + fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return")) + fmt.Scanln() // wait for enter press + + if err := gocui.Screen.Resume(); err != nil { + panic(err) + } +} + +func (app *App) layout(g *gocui.Gui) error { + maxX, maxY := g.Size() + descriptionViewHeight := 7 + keybindingsViewHeight := 3 + editorViewHeight := 3 + if !app.filtering { + editorViewHeight = 0 + } else { + descriptionViewHeight = 0 + keybindingsViewHeight = 0 + } + g.Cursor = app.filtering + g.FgColor = gocui.ColorGreen + listView, err := g.SetView("list", 0, 0, maxX-1, maxY-descriptionViewHeight-keybindingsViewHeight-editorViewHeight-1, 0) + if err != nil { + if err.Error() != "unknown view" { + return err + } + listView.Highlight = true + listView.Clear() + for _, test := range app.tests { + fmt.Fprintln(listView, test.Name()) + } + listView.Title = "Tests" + listView.FgColor = gocui.ColorDefault + if _, err := g.SetCurrentView("list"); err != nil { + return err + } + } + + descriptionView, err := g.SetViewBeneath("description", "list", descriptionViewHeight) + if err != nil { + if err.Error() != "unknown view" { + return err + } + descriptionView.Title = "Test description" + descriptionView.Wrap = true + descriptionView.FgColor = gocui.ColorDefault + } + + keybindingsView, err := g.SetViewBeneath("keybindings", "description", keybindingsViewHeight) + if err != nil { + if err.Error() != "unknown view" { + return err + } + keybindingsView.Title = "Keybindings" + keybindingsView.Wrap = true + keybindingsView.FgColor = gocui.ColorDefault + fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, t: run test slow, s: sandbox, o: open test file, shift+o: open test snapshot directory, forward-slash: filter") + } + + editorView, err := g.SetViewBeneath("editor", "keybindings", editorViewHeight) + if err != nil { + if err.Error() != "unknown view" { + return err + } + editorView.Title = "Filter" + editorView.FgColor = gocui.ColorDefault + editorView.Editable = true + } + + currentTest := app.getCurrentTest() + if currentTest == nil { + return nil + } + + descriptionView.Clear() + fmt.Fprint(descriptionView, currentTest.Description()) + + if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if app.itemIdx < len(app.tests)-1 { + app.itemIdx++ + } + + listView, err := g.View("list") + if err != nil { + return err + } + listView.FocusPoint(0, app.itemIdx) + return nil + }); err != nil { + log.Panicln(err) + } + + return nil +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index ad4c942ea..da25b8f9d 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -1,99 +1,31 @@ package types import ( + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/types" ) -// TODO: refactor this so that we don't have code spread around so much. We want -// our TestImpl struct to take the dependencies it needs from the gui and then -// create the input, assert, shell structs itself. That way, we can potentially -// ditch these interfaces so that we don't need to keep updating them every time -// we add a method to the concrete struct. +// these interfaces are used by the gui package so that it knows what it needs +// to provide to a test in order for the test to run. -type Test interface { - Name() string - Description() string - // this is called before lazygit is run, for the sake of preparing the repo - SetupRepo(Shell) - // this gives you the default config and lets you set whatever values on it you like, - // so that they appear when lazygit runs +type IntegrationTest interface { + Run(GuiAdapter) SetupConfig(config *config.AppConfig) - // this is called upon lazygit starting - Run(types.GuiAdapter) - // e.g. '-debug' - ExtraCmdArgs() string - // for tests that are flakey and when we don't have time to fix them - Skip() bool } -// this is for running shell commands, mostly for the sake of setting up the repo -// but you can also run the commands from within lazygit to emulate things happening -// in the background. -// Implementation is at pkg/integration/shell.go -type Shell interface { - RunCommand(command string) Shell - CreateFile(name string, content string) Shell - NewBranch(branchName string) Shell - GitAdd(path string) Shell - GitAddAll() Shell - Commit(message string) Shell - EmptyCommit(message string) Shell - // convenience method for creating a file and adding it - CreateFileAndAdd(fileName string, fileContents string) Shell - // creates commits 01, 02, 03, ..., n with a new file in each - // The reason for padding with zeroes is so that it's easier to do string - // matches on the commit messages when there are many of them - CreateNCommits(n int) Shell -} - -// through this interface our test interacts with the lazygit gui -// Implementation is at pkg/gui/input.go -type Input interface { - // key is something like 'w' or ''. It's best not to pass a direct value, - // but instead to go through the default user config to get a more meaningful key name - PressKeys(keys ...string) - // for typing into a popup prompt - Type(content string) - // for when you want to allow lazygit to process something before continuing - Wait(milliseconds int) - // going straight to a particular side window - SwitchToStatusWindow() - SwitchToFilesWindow() - SwitchToBranchesWindow() - SwitchToCommitsWindow() - SwitchToStashWindow() - // i.e. pressing enter - Confirm() - // i.e. pressing escape - Cancel() - // i.e. pressing space - Select() - // i.e. pressing down arrow - NextItem() - // i.e. pressing up arrow - PreviousItem() - // this will look for a list item in the current panel and if it finds it, it will - // enter the keypresses required to navigate to it. - // The test will fail if: - // - the user is not in a list item - // - no list item is found containing the given text - // - multiple list items are found containing the given text in the initial page of items - NavigateToListItemContainingText(text string) - ContinueRebase() - ContinueMerge() -} - -// through this interface we assert on the state of the lazygit gui -// implementation is at pkg/gui/assert.go -type Assert interface { - WorkingTreeFileCount(int) - CommitCount(int) - HeadCommitMessage(string) - CurrentViewName(expectedViewName string) - CurrentBranchName(expectedBranchName string) - InListContext() - SelectedLineContains(text string) - // for when you just want to fail the test yourself - Fail(errorMessage string) +// this is the interface through which our integration tests interact with the lazygit gui +type GuiAdapter interface { + PressKey(string) + Keys() config.KeybindingConfig + CurrentContext() types.Context + Model() *types.Model + Fail(message string) + // These two log methods are for the sake of debugging while testing. There's no need to actually + // commit any logging. + // logs to the normal place that you log to i.e. viewable with `lazygit --logs` + Log(message string) + // logs in the actual UI (in the commands panel) + LogUI(message string) + CheckedOutRef() *models.Branch } diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/index b/test/integration_new/commit/commit/expected/repo/.git_keep/index index d72d7c0261d7e4a93b253031829ce67f8be3eaf1..31a81c209ded8e76ebf04cb356ca69b09526fb15 100644 GIT binary patch delta 92 zcmcb}c#%=X#WTp6fq{Vugp)qcpQ!U;=GhD!BK d%n1~c$ar^&_0*-z#Cg@fZJK|#s8uiG0svu-CFKAB delta 92 zcmcb}c#%=X#WTp6fq{Vugp=Mk2+42WuLYwS85mfZuN;0iQN>UkB(=4q1S|!lp;GTB d<^+leEU-S{->|=`D!;eI0sxE%BX|G+ diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/logs/HEAD b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/HEAD index cc260688d..925bffbb2 100644 --- a/test/integration_new/commit/commit/expected/repo/.git_keep/logs/HEAD +++ b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/HEAD @@ -1 +1 @@ -0000000000000000000000000000000000000000 460150760ff1f381c3f5769b919cb73107c5871a CI 1659863059 +1000 commit (initial): my commit message +0000000000000000000000000000000000000000 944b9ea58bef8f6352c3a081a1d0037125bcaabc CI 1660133266 +1000 commit (initial): my commit message diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master index cc260688d..925bffbb2 100644 --- a/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master +++ b/test/integration_new/commit/commit/expected/repo/.git_keep/logs/refs/heads/master @@ -1 +1 @@ -0000000000000000000000000000000000000000 460150760ff1f381c3f5769b919cb73107c5871a CI 1659863059 +1000 commit (initial): my commit message +0000000000000000000000000000000000000000 944b9ea58bef8f6352c3a081a1d0037125bcaabc CI 1660133266 +1000 commit (initial): my commit message diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a b/test/integration_new/commit/commit/expected/repo/.git_keep/objects/46/0150760ff1f381c3f5769b919cb73107c5871a deleted file mode 100644 index 4792ad56dc3321f51e627c8d96a72613f74b3d3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125 zcmV-@0D}K`0ga783c@fD06pgwdlw|hcGm<%gr540HQlPpwtXNW4u3-|1T^uoTN|@Lq1#_Vx^qAe8pKLbYY^vDN$li5u=lLy_W@s5zfQd>n8Sk>LC}q05W@| erZeo(d+!=;ld6B}&OY!ZrtS-@h%ePg06OXb literal 0 HcmV?d00001 diff --git a/test/integration_new/commit/commit/expected/repo/.git_keep/refs/heads/master b/test/integration_new/commit/commit/expected/repo/.git_keep/refs/heads/master index a21004dfc..7b10e3bcb 100644 --- a/test/integration_new/commit/commit/expected/repo/.git_keep/refs/heads/master +++ b/test/integration_new/commit/commit/expected/repo/.git_keep/refs/heads/master @@ -1 +1 @@ -460150760ff1f381c3f5769b919cb73107c5871a +944b9ea58bef8f6352c3a081a1d0037125bcaabc diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/HEAD b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/HEAD index b641844cb..189a2b0bc 100644 --- a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/HEAD +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/HEAD @@ -1,4 +1,4 @@ -0000000000000000000000000000000000000000 470038e1336649b2965305f9f6a82501a836810e CI 1659865912 +1000 commit (initial): commit 1 -470038e1336649b2965305f9f6a82501a836810e c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 commit: commit 2 -c8bec8f2b323cbb476e708bd10c145ea7cc9f726 62a60693a2e154e745ee353f67a05156d0532c23 CI 1659865912 +1000 commit: commit 3 -62a60693a2e154e745ee353f67a05156d0532c23 c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 checkout: moving from master to my-branch-name +0000000000000000000000000000000000000000 4e72cd440eec154569568bff8d4c955052ae246c CI 1660125381 +1000 commit (initial): commit 1 +4e72cd440eec154569568bff8d4c955052ae246c 563414ba32c967cfbe21a17fe892d6118c1c58e8 CI 1660125381 +1000 commit: commit 2 +563414ba32c967cfbe21a17fe892d6118c1c58e8 0af36e404e6fec1c3a4d887e30622238e5ea0b2b CI 1660125381 +1000 commit: commit 3 +0af36e404e6fec1c3a4d887e30622238e5ea0b2b 563414ba32c967cfbe21a17fe892d6118c1c58e8 CI 1660125382 +1000 checkout: moving from master to my-branch-name diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master index 57ad94e9f..0e17a4008 100644 --- a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/master @@ -1,3 +1,3 @@ -0000000000000000000000000000000000000000 470038e1336649b2965305f9f6a82501a836810e CI 1659865912 +1000 commit (initial): commit 1 -470038e1336649b2965305f9f6a82501a836810e c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 commit: commit 2 -c8bec8f2b323cbb476e708bd10c145ea7cc9f726 62a60693a2e154e745ee353f67a05156d0532c23 CI 1659865912 +1000 commit: commit 3 +0000000000000000000000000000000000000000 4e72cd440eec154569568bff8d4c955052ae246c CI 1660125381 +1000 commit (initial): commit 1 +4e72cd440eec154569568bff8d4c955052ae246c 563414ba32c967cfbe21a17fe892d6118c1c58e8 CI 1660125381 +1000 commit: commit 2 +563414ba32c967cfbe21a17fe892d6118c1c58e8 0af36e404e6fec1c3a4d887e30622238e5ea0b2b CI 1660125381 +1000 commit: commit 3 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name index 7e8364dfe..6f401d926 100644 --- a/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/logs/refs/heads/my-branch-name @@ -1 +1 @@ -0000000000000000000000000000000000000000 c8bec8f2b323cbb476e708bd10c145ea7cc9f726 CI 1659865912 +1000 branch: Created from c8bec8f2b323cbb476e708bd10c145ea7cc9f726 +0000000000000000000000000000000000000000 563414ba32c967cfbe21a17fe892d6118c1c58e8 CI 1660125382 +1000 branch: Created from 563414ba32c967cfbe21a17fe892d6118c1c58e8 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/0a/f36e404e6fec1c3a4d887e30622238e5ea0b2b b/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/0a/f36e404e6fec1c3a4d887e30622238e5ea0b2b new file mode 100644 index 0000000000000000000000000000000000000000..eb9800f0aabfc5ef2960db03868574081aa3da8d GIT binary patch literal 147 zcmV;E0Brww0gaA93c@fD06pgwdlw|TN!AUB2tD-~$##o^v86=t`S#!gyw1#FbZPC; z0dqR^%?x4{N|SOVt!yxWUgj0`{>rP*{eh15HZ#gNHd>x*MtSnvq`3j z5k*mxn7H!p_ho}&gv&7A>{HtMw39CF3drn{QV@hAdhgv%_o;4w=D!|-`vM@{I%#lq BN{j#i literal 0 HcmV?d00001 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e b/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/47/0038e1336649b2965305f9f6a82501a836810e deleted file mode 100644 index 5099d76b85b09a9905b8b7a4b4cf9a4b0763ae96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 zcmV-+0Ez#20ga783c@fD06pgwdlzIAHtPmNgr540Y`R6k5GfISeskypJj`Vnn9^!} zb?|gO_00@OCB&sLg`#YlGBR2uU1i}EL{>GUgyizKKidY=47X{1*e}=5+b*TmJ9v&s Y20p;qBcdC2OuGG3WMaF8}}l literal 0 HcmV?d00001 diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/56/3414ba32c967cfbe21a17fe892d6118c1c58e8 b/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/56/3414ba32c967cfbe21a17fe892d6118c1c58e8 new file mode 100644 index 000000000..1a610226f --- /dev/null +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/objects/56/3414ba32c967cfbe21a17fe892d6118c1c58e8 @@ -0,0 +1,2 @@ +xÎA +1 @Q×=Eö‚¤1‰-ˆ®æm&ƒ‚u†¡‚ÇwÀíç-¾Í­=:Ä,»¾º×D2š2YU¯¹ ¢ ×)š)¥©ºsFKYýÕýD62£»EaÑ,šê´q¶,‚BʼnÕBy÷û¼Âm€óm¸ú§´åé›Û¢*F’cаˆ¶ºMuÿ“ÿ&IKcC@LXw$*%4J%^%=d{%C4^tLaaj7@!$qw_9<#Y;>Ds^ zAG9QqT$78Yy!(CKVVYr_=9_*>+n#!nwOzr+km \ No newline at end of file diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master index 5c73ff150..003b5b1fd 100644 --- a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/master @@ -1 +1 @@ -62a60693a2e154e745ee353f67a05156d0532c23 +0af36e404e6fec1c3a4d887e30622238e5ea0b2b diff --git a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name index 75da99d74..7d25fb5a8 100644 --- a/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name +++ b/test/integration_new/commit/new_branch/expected/repo/.git_keep/refs/heads/my-branch-name @@ -1 +1 @@ -c8bec8f2b323cbb476e708bd10c145ea7cc9f726 +563414ba32c967cfbe21a17fe892d6118c1c58e8 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG index 9e0c89102..b572ccaf3 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG @@ -14,14 +14,14 @@ commit 05 # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # -# Date: Mon Aug 8 21:32:34 2022 +1000 +# Date: Wed Aug 10 19:26:28 2022 +1000 # -# interactive rebase in progress; onto a1a6f7b +# interactive rebase in progress; onto cc9defb # Last commands done (4 commands done): -# drop 84b1ae9 commit 04 -# squash aa2585a commit 05 +# drop da71be1 commit 04 +# squash 8a38398 commit 05 # No commands remaining. -# You are currently rebasing branch 'master' on 'a1a6f7b'. +# You are currently rebasing branch 'master' on 'cc9defb'. # # Changes to be committed: # new file: file02.txt diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD index 42745976b..319624f9d 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/ORIG_HEAD @@ -1 +1 @@ -aa2585aff7d2278341ca816f187e623503d7c4fb +8a3839811a7a9f4c678090c9def892d1e7ad7e54 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD index 42745976b..319624f9d 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/REBASE_HEAD @@ -1 +1 @@ -aa2585aff7d2278341ca816f187e623503d7c4fb +8a3839811a7a9f4c678090c9def892d1e7ad7e54 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/index b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/index index ce7858b100125bd62c61344d33363fcaadd1dd38..2a0f3f08aec0c1d2e7524875027e186182dda6f0 100644 GIT binary patch delta 147 zcmX@Ze1=)Y#WTp6fq{Vuh*^?8R~|8$_Pi5DGcqu+GT*juny6wa{<-p~HCy)tpcI&f zNI7s!ECER!lWaIt4U_`YP$`*-M<7z~x{Se6KpHA#I9Y^oiOABmTQ@BI$^PwwcGIE% MuV?)z-u}810Pb!*PXGV_ delta 147 zcmX@Ze1=)Y#WTp6fq{Vuh*^?8d# zrLIjZ0ZYj&oALuB1)`x+FDD)WOUX)_-Ums6XsFb`$s&wPM7mYeOfCHar`<5BJmeSp KCDicEC3OH(B0LrV diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/HEAD index 0dda59a30..1d8d45a03 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/HEAD +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/HEAD @@ -1,10 +1,10 @@ -0000000000000000000000000000000000000000 a1a6f7bda6aeaa08ec75f590845780fde90d901c CI 1659958354 +1000 commit (initial): commit 01 -a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958354 +1000 commit: commit 02 -cb7e56856ecee89fa44c613e094fcf962fe18cf1 6741ab4fe22a3d36b6c64397fc4295dbae1ba71d CI 1659958354 +1000 commit: commit 03 -6741ab4fe22a3d36b6c64397fc4295dbae1ba71d 84b1ae9d83049341897c9388afffdc9049c3317f CI 1659958354 +1000 commit: commit 04 -84b1ae9d83049341897c9388afffdc9049c3317f aa2585aff7d2278341ca816f187e623503d7c4fb CI 1659958354 +1000 commit: commit 05 -aa2585aff7d2278341ca816f187e623503d7c4fb a1a6f7bda6aeaa08ec75f590845780fde90d901c CI 1659958355 +1000 rebase (start): checkout a1a6f7bda6aeaa08ec75f590845780fde90d901c -a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958355 +1000 rebase: fast-forward -cb7e56856ecee89fa44c613e094fcf962fe18cf1 9c68b57ac7b652fbebc5e93a9a1b72014400c269 CI 1659958355 +1000 rebase (continue) (fixup): # This is a combination of 2 commits. -9c68b57ac7b652fbebc5e93a9a1b72014400c269 f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (squash): commit 02 -f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (finish): returning to refs/heads/master +0000000000000000000000000000000000000000 cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 CI 1660123588 +1000 commit (initial): commit 01 +cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 2e2cd25ffdec58d32b5d549f8402bd054e22cc2a CI 1660123588 +1000 commit: commit 02 +2e2cd25ffdec58d32b5d549f8402bd054e22cc2a 90fda12ce101e7d0d4594a879e5bbd1be3c857a8 CI 1660123588 +1000 commit: commit 03 +90fda12ce101e7d0d4594a879e5bbd1be3c857a8 da71be1afbb03f46e91ab5de17d69f148bb009f3 CI 1660123588 +1000 commit: commit 04 +da71be1afbb03f46e91ab5de17d69f148bb009f3 8a3839811a7a9f4c678090c9def892d1e7ad7e54 CI 1660123589 +1000 commit: commit 05 +8a3839811a7a9f4c678090c9def892d1e7ad7e54 cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 CI 1660123589 +1000 rebase (start): checkout cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 +cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 2e2cd25ffdec58d32b5d549f8402bd054e22cc2a CI 1660123589 +1000 rebase: fast-forward +2e2cd25ffdec58d32b5d549f8402bd054e22cc2a b85535ebf12659044c33386376121d76756ceb59 CI 1660123590 +1000 rebase (continue) (fixup): # This is a combination of 2 commits. +b85535ebf12659044c33386376121d76756ceb59 aba3469fd6fc584a6af9c0073873005ffaaea56c CI 1660123590 +1000 rebase (continue) (squash): commit 02 +aba3469fd6fc584a6af9c0073873005ffaaea56c aba3469fd6fc584a6af9c0073873005ffaaea56c CI 1660123590 +1000 rebase (continue) (finish): returning to refs/heads/master diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master index 89309e282..c6c18ee5a 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/logs/refs/heads/master @@ -1,6 +1,6 @@ -0000000000000000000000000000000000000000 a1a6f7bda6aeaa08ec75f590845780fde90d901c CI 1659958354 +1000 commit (initial): commit 01 -a1a6f7bda6aeaa08ec75f590845780fde90d901c cb7e56856ecee89fa44c613e094fcf962fe18cf1 CI 1659958354 +1000 commit: commit 02 -cb7e56856ecee89fa44c613e094fcf962fe18cf1 6741ab4fe22a3d36b6c64397fc4295dbae1ba71d CI 1659958354 +1000 commit: commit 03 -6741ab4fe22a3d36b6c64397fc4295dbae1ba71d 84b1ae9d83049341897c9388afffdc9049c3317f CI 1659958354 +1000 commit: commit 04 -84b1ae9d83049341897c9388afffdc9049c3317f aa2585aff7d2278341ca816f187e623503d7c4fb CI 1659958354 +1000 commit: commit 05 -aa2585aff7d2278341ca816f187e623503d7c4fb f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 CI 1659958355 +1000 rebase (continue) (finish): refs/heads/master onto a1a6f7bda6aeaa08ec75f590845780fde90d901c +0000000000000000000000000000000000000000 cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 CI 1660123588 +1000 commit (initial): commit 01 +cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 2e2cd25ffdec58d32b5d549f8402bd054e22cc2a CI 1660123588 +1000 commit: commit 02 +2e2cd25ffdec58d32b5d549f8402bd054e22cc2a 90fda12ce101e7d0d4594a879e5bbd1be3c857a8 CI 1660123588 +1000 commit: commit 03 +90fda12ce101e7d0d4594a879e5bbd1be3c857a8 da71be1afbb03f46e91ab5de17d69f148bb009f3 CI 1660123588 +1000 commit: commit 04 +da71be1afbb03f46e91ab5de17d69f148bb009f3 8a3839811a7a9f4c678090c9def892d1e7ad7e54 CI 1660123589 +1000 commit: commit 05 +8a3839811a7a9f4c678090c9def892d1e7ad7e54 aba3469fd6fc584a6af9c0073873005ffaaea56c CI 1660123590 +1000 rebase (continue) (finish): refs/heads/master onto cc9defb8ae9134f1a9a6c28a0006dc8c8cd78347 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/2e/2cd25ffdec58d32b5d549f8402bd054e22cc2a b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/2e/2cd25ffdec58d32b5d549f8402bd054e22cc2a new file mode 100644 index 000000000..20504e122 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/2e/2cd25ffdec58d32b5d549f8402bd054e22cc2a @@ -0,0 +1,3 @@ +xÎA +Â0P×9ÅìÉ$1™€ˆÐU1™LQ0¶”ß,<€üÝç}ø²¶öè€9ú® +l„âc‰T—d ñì9Y¹—T\И‹Ùx×W‘\u)Ěч¡2GqÄÖÚX…Fj"’áw¿¯;L3\¦ù¦nÛSO²¶+`Œ?ÁÇÐŒvœêú'ÿy°Î|̱;u \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d deleted file mode 100644 index 7aeda0bfd..000000000 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/67/41ab4fe22a3d36b6c64397fc4295dbae1ba71d +++ /dev/null @@ -1,3 +0,0 @@ -xÎM -Â0†a×9Åì™ÉŸºê1’ô -Ö–ÁãÛ…pûò,ÞºÌó£“h<ô Ä©H ©ÚRòä…½æ„æTØEÆÞ¬Àš5oxuªåŒSˆ¨@Ò–½¯QX}«M£mT›˜üî÷e£a¤Ë0ÞðÉóúÄ©.ó•$Õ\ðtf6{ݧ:þä?OìÌ;ß \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f deleted file mode 100644 index cd394caaf..000000000 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/84/b1ae9d83049341897c9388afffdc9049c3317f +++ /dev/null @@ -1,3 +0,0 @@ -xÎM -Â0@a×9ÅìI2?m@DèÊcL’ -Ö–ÁãÛ…pûø¯,óüè’úfÈU…[L¹øL£êèsEõ±´&K#dnݪ›½:È@A35‹Q±¢d)B˜†V(&®Y-dBuúî÷eƒéçévµÎëÓNe™/„Sâ™à¼÷n¯ûT·?ùσ'÷Ìu; \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/8a/3839811a7a9f4c678090c9def892d1e7ad7e54 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/8a/3839811a7a9f4c678090c9def892d1e7ad7e54 new file mode 100644 index 0000000000000000000000000000000000000000..f518dcc891b4a1d8e315e04858939d0e992b8d3c GIT binary patch literal 147 zcmV;E0Brww0gcW<3c@fDg<;n@#q0%{X`G}3B0^U^#w4AfU~DN7Jib|Y0JlFMkC)e4 z9}P&tq3vd114$f>RZH=ahqRcb;5kzW(K1Ws)El`?cY8HhG*U9sk`nrI4ko0DJBUk= zLg%q}l;WMf+kM?(n&C3dH~VPao_5M>y+WTx8a$5zM?yr`Yd_WOPyX|O?7lEGJQ%Lg BL@NLQ literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/90/fda12ce101e7d0d4594a879e5bbd1be3c857a8 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/90/fda12ce101e7d0d4594a879e5bbd1be3c857a8 new file mode 100644 index 0000000000000000000000000000000000000000..71b49be646369d4e7de115a10e11ce84da88652d GIT binary patch literal 147 zcmV;E0Brww0gcX03c@fDKw;N8#q0%{nPfr&QiQI0jQmVcFt(Hk9^WoJfZNBncxCPV zF@T~!ja?Pg*Gx@9;haTNb%ZXapv^`?>rs!!q-?gd>uUfjm&(#w)gqx<&Qit2794V} zC{CG6;bhW#+}9oE1+MdQ(@*N#Q!i!h6Oi{vY$XIZ6C#?U9#cdA17 literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 deleted file mode 100644 index c97722a50..000000000 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/9c/68b57ac7b652fbebc5e93a9a1b72014400c269 +++ /dev/null @@ -1,2 +0,0 @@ -x…ŽÁJÄ0†=ç)z–IÚ´‰È²°§½û“fbÃ6mi"úøF© ^„9}óý3ÿ¸¦ ´ª{(;3pï ÚŒÊ9Íä;‰%áµÛ¹2%Y‰v^ -¤> ÎSOL„†ÇAmÑtz0<[ôå(è­Lë×<_oþ ´Í|×tÙkkµiuE¥µTát}袗)f¨CP³..TâºÀ@}z,Ÿ~Yeb¹+Hœ3½ò“8*ñmóïqžÁ1ä{Ü6ö5ÔüXØŠOz d× \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/a1/a6f7bda6aeaa08ec75f590845780fde90d901c deleted file mode 100644 index 5405907585e20ec827c780274551074deb299a40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 zcmV-*0E+*30gcT;3c@fDMq$@E#q0%{$z*DNC{pOE$EanBf+12OcznC)0o*=5cuOti z*+^18wauN(RB#n2B6HI9&S_Owc#a0?0ESuQ6y{HR)Q!f8`f(ckJD2y%FH0>qlFk6j XYKhLm%wezlr1_ux@Wc{+m!m5<*%dn* diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb deleted file mode 100644 index 9a3b44f36..000000000 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/aa/2585aff7d2278341ca816f187e623503d7c4fb +++ /dev/null @@ -1,4 +0,0 @@ -xÎA -1 …a×=E÷‚4“¦m@D˜•Çh3) -Ö† -ß.<€ÛÿÁ“µµG·ÀáÐwU«pLJ9׊‘¦²¨–€$BT!*ÃÀly×W·ÉÈÊKBç=$ŽÂ˜Ò˜×Ex  B¬&¿û}Ýí|³çùvÕOnÛSO²¶‹…@Ì”¼=‚sÎ §ºþ™ÿzëÈ|²);= \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/ab/a3469fd6fc584a6af9c0073873005ffaaea56c b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/ab/a3469fd6fc584a6af9c0073873005ffaaea56c new file mode 100644 index 000000000..6f0bc9bd3 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/ab/a3469fd6fc584a6af9c0073873005ffaaea56c @@ -0,0 +1,3 @@ +x}ŽÍ +Â0„=ç)ö.Èæwzêc¬Û- +Æ–ÁÇ7‡âQfÃð Œ,µ>8¤CÛTA‹ÏÈ^(: 7RŠa–€N4:ŽJän³ò¦¯"eÒù–Y‹õa¶\8‰ËŒˆi’Ü5Qö ¿Û}Ù`á<ŒWýp]Ÿz’¥^À¦„Öù˜3mšÞöSMÿãw|çùÅh¾ý-=ä \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/b8/5535ebf12659044c33386376121d76756ceb59 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/b8/5535ebf12659044c33386376121d76756ceb59 new file mode 100644 index 0000000000000000000000000000000000000000..085f6e554c7bedf8a851154a62a572ea46d24243 GIT binary patch literal 222 zcmV<403rW)0ez3nN(3ue_ z9lzmhfaf_?=!?+3KCReNS)oiIKZSm*9)3}6CJ?oU|1O`YR(zocc6$t5vM-wc5HLu1 YOUnYDpuVPUqu5Qu55*Px0BAL2w#iUv`2YX_ literal 0 HcmV?d00001 diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cb/7e56856ecee89fa44c613e094fcf962fe18cf1 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cb/7e56856ecee89fa44c613e094fcf962fe18cf1 deleted file mode 100644 index 0145803afca2593db5eafb666e5c47b60f86928b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmV;F0Biqv0gcW*3IZ_@Kw;~gVtPSlviX^Sh_KZjW0FizFz&KM@c3GI0Nck`yt4Lw zA3!M_#;yv2wzxcn9JscKOysR)TN;VvA~|O3G^A`c+4VUz`X~Q+Kx;nDg*%Rv CfJjCF diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cc/9defb8ae9134f1a9a6c28a0006dc8c8cd78347 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/cc/9defb8ae9134f1a9a6c28a0006dc8c8cd78347 new file mode 100644 index 0000000000000000000000000000000000000000..3fa86426f6c1e98f9b4004af4158effa1a0d9a8b GIT binary patch literal 117 zcmV-*0E+*30gcT;3c@fDMq$@E#q0(7CzFX8C?a&#W7IN5!4N4CJic9c0Jo11-qLD) zbmCMFeRC&OVLP*lMIgy~Lt4efyhJt5W-!%=6y|row~fY$hH<+3XRfcOpO#iH#5seb XXeo4L0Aa8Dr2C)z=LsCXkõT–~ +É:ŽŽ„ˆf¯ûÔÐ?ùÏzóÏ£;v \ No newline at end of file diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/objects/f4/316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 deleted file mode 100644 index b48b3e823d77f4deac1f8366dd9b2072876505b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153 zcmV;K0A~Mq0fmmu3c@fDggxge_AbcgXS)fA2tD;N)^vk{v86=t@y(%l73T0Ud;^o0 zS|1G@@}X_2pvgxRpP8J78)sIgP;x+*m7GV%iKNG@vgxaVSQ!f6W*J09Flt{GN*Dui z#4>BbIibx)-tE3@FivnDr>lOXu1`JXrCz`eloCZx0ge_CO>g_DrhjzW_1y<_=7(gy H01`e0?axPZ diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master index 4190292c7..93319ec4d 100644 --- a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/refs/heads/master @@ -1 +1 @@ -f4316f7a6df3fe5b7e8da1b2c8767ed1e825dc05 +aba3469fd6fc584a6af9c0073873005ffaaea56c From a45b22e12f60f1e3d4d1caf2fd3abc14d1cd636d Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 11 Aug 2022 21:04:10 +1000 Subject: [PATCH 06/16] re-name Input and improve documentation --- pkg/integration/README.md | 69 ++++++++++++++++++- pkg/integration/deprecated/tui/main.go | 10 +-- pkg/integration/helpers/input.go | 44 ++++++------ pkg/integration/helpers/test.go | 18 +++-- pkg/integration/runner/main.go | 31 +++++---- pkg/integration/tests/branch/suggestions.go | 2 +- pkg/integration/tests/commit/commit.go | 2 +- pkg/integration/tests/commit/new_branch.go | 2 +- .../tests/interactive_rebase/one.go | 2 +- 9 files changed, 128 insertions(+), 52 deletions(-) diff --git a/pkg/integration/README.md b/pkg/integration/README.md index 55481ebe6..e8df3f190 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -4,8 +4,71 @@ There's a lot happening in this package so it's worth a proper explanation. This package is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected. +## Writing tests + +The tests live in pkg/integration/tests. Each test has two important steps: the setup step and the run step. + +### Setup step + +In the setup step, we prepare a repo with shell commands, for example, creating a merge conflict that will need to be resolved upon opening lazygit. This is all done via the `shell` argument. + +### Run step + +The run step has four arguments passed in: + +1. `shell` +2. `input` +3. `assert` +4. `keys` + +`shell` we've already seen in the setup step. The reason it's passed into the run step is that we may want to emulate background events. For example, the user modifying a file outside of lazygit. + +`input` is for driving the gui by pressing certain keys, selecting list items, etc. + +`assert` is for asserting on the state of the lazygit session. When you call a method on `assert`, the assert struct will wait for the assertion to hold true and then continue (failing the test after a timeout). For this reason, assertions have two purposes: one is to ensure the test fails as soon as something unexpected happens, but another is to allow lazygit to process a keypress before you follow up with more keypresses. If you input a bunch of keypresses too quickly lazygit might get confused. + +### Tips + +Try to do as much setup work as possible in your setup step. For example, if all you're testing is that the user is able to resolve merge conflicts, create the merge conflicts in the setup step. On the other hand, if you're testing to see that lazygit can warn the user about merge conflicts after an attempted merge, it's fine to wait until the run step to actually create the conflicts. If the run step is focused on the thing you're trying to test, the test will run faster and its intent will be clearer. + +Use assertions to ensure that lazygit has processed all your keybindings so far. For example, if you press 'n' on a branch to create a new branch, assert that the confirmation view is now focused. + +If you find yourself doing something frequently in a test, consider making it a method in one of the helper arguments. For example, instead of calling `input.PressKey(keys.Universal.Confirm)` in 100 places, it's better to have a method `input.Confirm()`. This is not to say that everything should be made into a method on the input struct: just things that are particularly common in tests. + +## Running tests + There are three ways to invoke a test: -1. go run pkg/integration/runner/main.go commit/new_branch -2. go test pkg/integration/integration_test.go -3. +1. go run pkg/integration/runner/main.go [...] +2. go run pkg/integration/tui/main.go +3. go test pkg/integration/integration_test.go + +The first, the test runner, is for directly running a test from the command line. +The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. +The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output. + +The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. + +You can pass the KEY_PRESS_DELAY env var to the first command in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. + +## Snapshots + +At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`. Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=updateSnapshot to the test runner or the go test command. This is useful when you've made a change to + +## Sandbox mode + +Say you want to do a manual test of how lazygit handles merge-conflicts, but you can't be bothered actually finding a way to create merge conflicts in a repo. To make your life easier, you can simply run a merge-conflicts test in sandbox mode, meaning the setup step is run for you, and then instead of the test driving the lazygit session, you're allowed to drive it yourself. + +To run a test in sandbox mode you can press 's' on a test in the test TUI or pass the env var MODE=sandbox to the test runner. + +## Migration process + +At the time of writing, most tests are created under an old approach, where you would record yourself in a lazygit session and then the test would replay the keybindings with the same timestamps. This old approach is great for writing tests quickly, but is much harder to maintain. It has to rely entirely on snapshots to determining if a test passes or fails, and can't do assertions along the way. It's also harder to grok what's the intention behind certain actions that take place within the test (e.g. was the recorder intentionally switching to another panel or was that just a misclick?). + +At the moment, all the deprecated test code lives in pkg/integration/deprecated. Hopefully in the very near future we migrate everything across so that we don't need to maintain two systems. + +We should never write any new tests under the old method, and if a given test breaks because of new functionality, it's best to simply rewrite it under the new approach. If you want to run a test for the sake of watching what it does so that you can transcribe it into the new approach, you can run: + +``` +go run pkg/integration/deprecated/tui/main.go +``` diff --git a/pkg/integration/deprecated/tui/main.go b/pkg/integration/deprecated/tui/main.go index aa698dc8e..b455c53bd 100644 --- a/pkg/integration/deprecated/tui/main.go +++ b/pkg/integration/deprecated/tui/main.go @@ -108,7 +108,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run test/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -122,7 +122,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run test/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -136,7 +136,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run test/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -150,7 +150,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run test/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -164,7 +164,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run test/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil diff --git a/pkg/integration/helpers/input.go b/pkg/integration/helpers/input.go index 8c34fab5c..55e1f8527 100644 --- a/pkg/integration/helpers/input.go +++ b/pkg/integration/helpers/input.go @@ -10,15 +10,15 @@ import ( integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) -type Impl struct { +type Input struct { gui integrationTypes.GuiAdapter keys config.KeybindingConfig assert *Assert pushKeyDelay int } -func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Impl { - return &Impl{ +func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input { + return &Input{ gui: gui, keys: keys, assert: assert, @@ -28,89 +28,89 @@ func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, ass // key is something like 'w' or ''. It's best not to pass a direct value, // but instead to go through the default user config to get a more meaningful key name -func (self *Impl) PressKeys(keyStrs ...string) { +func (self *Input) PressKeys(keyStrs ...string) { for _, keyStr := range keyStrs { self.pressKey(keyStr) } } -func (self *Impl) pressKey(keyStr string) { +func (self *Input) pressKey(keyStr string) { self.Wait(self.pushKeyDelay) self.gui.PressKey(keyStr) } -func (self *Impl) SwitchToStatusWindow() { +func (self *Input) SwitchToStatusWindow() { self.pressKey(self.keys.Universal.JumpToBlock[0]) } -func (self *Impl) SwitchToFilesWindow() { +func (self *Input) SwitchToFilesWindow() { self.pressKey(self.keys.Universal.JumpToBlock[1]) } -func (self *Impl) SwitchToBranchesWindow() { +func (self *Input) SwitchToBranchesWindow() { self.pressKey(self.keys.Universal.JumpToBlock[2]) } -func (self *Impl) SwitchToCommitsWindow() { +func (self *Input) SwitchToCommitsWindow() { self.pressKey(self.keys.Universal.JumpToBlock[3]) } -func (self *Impl) SwitchToStashWindow() { +func (self *Input) SwitchToStashWindow() { self.pressKey(self.keys.Universal.JumpToBlock[4]) } -func (self *Impl) Type(content string) { +func (self *Input) Type(content string) { for _, char := range content { self.pressKey(string(char)) } } // i.e. pressing enter -func (self *Impl) Confirm() { +func (self *Input) Confirm() { self.pressKey(self.keys.Universal.Confirm) } // i.e. pressing escape -func (self *Impl) Cancel() { +func (self *Input) Cancel() { self.pressKey(self.keys.Universal.Return) } // i.e. pressing space -func (self *Impl) Select() { +func (self *Input) Select() { self.pressKey(self.keys.Universal.Select) } // i.e. pressing down arrow -func (self *Impl) NextItem() { +func (self *Input) NextItem() { self.pressKey(self.keys.Universal.NextItem) } // i.e. pressing up arrow -func (self *Impl) PreviousItem() { +func (self *Input) PreviousItem() { self.pressKey(self.keys.Universal.PrevItem) } -func (self *Impl) ContinueMerge() { +func (self *Input) ContinueMerge() { self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) self.assert.SelectedLineContains("continue") self.Confirm() } -func (self *Impl) ContinueRebase() { +func (self *Input) ContinueRebase() { self.ContinueMerge() } // for when you want to allow lazygit to process something before continuing -func (self *Impl) Wait(milliseconds int) { +func (self *Input) Wait(milliseconds int) { time.Sleep(time.Duration(milliseconds) * time.Millisecond) } -func (self *Impl) LogUI(message string) { +func (self *Input) LogUI(message string) { self.gui.LogUI(message) } -func (self *Impl) Log(message string) { +func (self *Input) Log(message string) { self.gui.LogUI(message) } @@ -125,7 +125,7 @@ func (self *Impl) Log(message string) { // If this changes in future, we'll need to update this code to first attempt to find the item // in the current page and failing that, jump to the top of the view and iterate through all of it, // looking for the item. -func (self *Impl) NavigateToListItemContainingText(text string) { +func (self *Input) NavigateToListItemContainingText(text string) { self.assert.InListContext() currentContext := self.gui.CurrentContext().(types.IListContext) diff --git a/pkg/integration/helpers/test.go b/pkg/integration/helpers/test.go index 6e5c45886..972bb1d5b 100644 --- a/pkg/integration/helpers/test.go +++ b/pkg/integration/helpers/test.go @@ -21,7 +21,7 @@ type Test struct { setupConfig func(config *config.AppConfig) run func( shell *Shell, - input *Impl, + input *Input, assert *Assert, keys config.KeybindingConfig, ) @@ -30,12 +30,18 @@ type Test struct { var _ integrationTypes.IntegrationTest = &Test{} type NewTestArgs struct { - Description string - SetupRepo func(shell *Shell) - SetupConfig func(config *config.AppConfig) - Run func(shell *Shell, input *Impl, assert *Assert, keys config.KeybindingConfig) + // Briefly describes what happens in the test and what it's testing for + Description string + // prepares a repo for testing + SetupRepo func(shell *Shell) + // takes a config and mutates. The mutated context will end up being passed to the gui + SetupConfig func(config *config.AppConfig) + // runs the test + Run func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) + // additional args passed to lazygit ExtraCmdArgs string - Skip bool + // for when a test is flakey + Skip bool } func NewTest(args NewTestArgs) *Test { diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index 07d1bf28f..217ca9cb0 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" + "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) @@ -20,27 +21,33 @@ import ( func main() { mode := integration.GetModeFromEnv() includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" - selectedTestName := os.Args[1] + var testsToRun []*helpers.Test - // check if our given test name actually exists - if selectedTestName != "" { - found := false - for _, test := range integration.Tests { - if test.Name() == selectedTestName { - found = true - break + if len(os.Args) > 1 { + outer: + for _, testName := range os.Args[1:] { + // check if our given test name actually exists + for _, test := range integration.Tests { + if test.Name() == testName { + testsToRun = append(testsToRun, test) + continue outer + } } + log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests.go`?", testName) } - if !found { - log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests.go`?", selectedTestName) - } + } else { + testsToRun = integration.Tests } + testNames := slices.Map(testsToRun, func(test *helpers.Test) string { + return test.Name() + }) + err := integration.RunTestsNew( log.Printf, runCmdInTerminal, func(test *helpers.Test, f func() error) { - if selectedTestName != "" && test.Name() != selectedTestName { + if !slices.Contains(testNames, test.Name()) { return } if err := f(); err != nil { diff --git a/pkg/integration/tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go index edceb5e8e..f4253cf82 100644 --- a/pkg/integration/tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -20,7 +20,7 @@ var Suggestions = helpers.NewTest(helpers.NewTestArgs{ NewBranch("other-new-branch-2"). NewBranch("other-new-branch-3") }, - Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() input.PressKeys(keys.Branches.CheckoutBranchByName) diff --git a/pkg/integration/tests/commit/commit.go b/pkg/integration/tests/commit/commit.go index 932d9cf6f..8aa0501e5 100644 --- a/pkg/integration/tests/commit/commit.go +++ b/pkg/integration/tests/commit/commit.go @@ -14,7 +14,7 @@ var Commit = helpers.NewTest(helpers.NewTestArgs{ shell.CreateFile("myfile", "myfile content") shell.CreateFile("myfile2", "myfile2 content") }, - Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { assert.CommitCount(0) input.Select() diff --git a/pkg/integration/tests/commit/new_branch.go b/pkg/integration/tests/commit/new_branch.go index e1e5090e8..f3ba36e2d 100644 --- a/pkg/integration/tests/commit/new_branch.go +++ b/pkg/integration/tests/commit/new_branch.go @@ -16,7 +16,7 @@ var NewBranch = helpers.NewTest(helpers.NewTestArgs{ EmptyCommit("commit 2"). EmptyCommit("commit 3") }, - Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { assert.CommitCount(3) input.SwitchToCommitsWindow() diff --git a/pkg/integration/tests/interactive_rebase/one.go b/pkg/integration/tests/interactive_rebase/one.go index d901b9c47..2b2acce37 100644 --- a/pkg/integration/tests/interactive_rebase/one.go +++ b/pkg/integration/tests/interactive_rebase/one.go @@ -14,7 +14,7 @@ var One = helpers.NewTest(helpers.NewTestArgs{ shell. CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 }, - Run: func(shell *helpers.Shell, input *helpers.Impl, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { input.SwitchToCommitsWindow() assert.CurrentViewName("commits") From ae798157d2b54b61415c5f184dc851d185b8b6fb Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 11 Aug 2022 21:17:01 +1000 Subject: [PATCH 07/16] update comments --- CONTRIBUTING.md | 13 ++- docs/Integration_Tests.md | 123 +--------------------- pkg/integration/README.md | 18 ++-- pkg/integration/deprecated/integration.go | 4 +- pkg/integration/deprecated/runner/main.go | 2 +- pkg/integration/deprecated/tui/main.go | 2 +- pkg/integration/runner/main.go | 10 +- pkg/integration/tui/main.go | 2 +- scripts/bisect.sh | 2 +- 9 files changed, 25 insertions(+), 151 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11fb75202..c6a68feae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,9 +63,9 @@ by setting [`formatting.gofumpt`](https://github.com/golang/tools/blob/master/go ```jsonc // .vscode/settings.json { - "gopls": { - "formatting.gofumpt": true - } + "gopls": { + "formatting.gofumpt": true + } } ``` @@ -82,6 +82,7 @@ From most places in the codebase you have access to a logger e.g. `gui.Log.Warn( If you find that the existing logs are too noisy, you can set the log level with e.g. `LOG_LEVEL=warn go run main.go -debug` and then only use `Warn` logs yourself. If you need to log from code in the vendor directory (e.g. the `gocui` package), you won't have access to the logger, but you can easily add logging support by adding the following: + ```go func newLogger() *logrus.Entry { // REPLACE THE BELOW PATH WITH YOUR ACTUAL LOG PATH (YOU'LL SEE THIS PRINTED WHEN YOU RUN `lazygit --logs` @@ -118,9 +119,7 @@ If you want to trigger a debug session from VSCode, you can use the following sn "request": "launch", "mode": "auto", "program": "main.go", - "args": [ - "--debug" - ], + "args": ["--debug"], "console": "externalTerminal" // <-- you need this to actually see the lazygit UI in a window while debugging } ] @@ -129,7 +128,7 @@ If you want to trigger a debug session from VSCode, you can use the following sn ## Testing -Lazygit has two kinds of tests: unit tests and integration tests. Unit tests go in files that end in `_test.go`, and are written in Go. Lazygit has its own integration test system where you can build a sandbox repo with a shell script, record yourself doing something, and commit the resulting repo snapshot. It's pretty damn cool! To learn more see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Integration_Tests.md) +Lazygit has two kinds of tests: unit tests and integration tests. Unit tests go in files that end in `_test.go`, and are written in Go. For integration tests, see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) ## Updating Gocui diff --git a/docs/Integration_Tests.md b/docs/Integration_Tests.md index 2cbf8064a..bbc2ebe9f 100644 --- a/docs/Integration_Tests.md +++ b/docs/Integration_Tests.md @@ -1,122 +1 @@ -# How To Make And Run Integration Tests For lazygit - -Integration tests are located in `test/integration`. Each test will run a bash script to prepare a test repo, then replay a recorded lazygit session from within that repo, and then the resultant repo will be compared to an expected repo that was created upon the initial recording. Each integration test lives in its own directory, and the name of the directory becomes the name of the test. Within the directory must be the following files: - -### `test.json` - -An example of a `test.json` is: - -``` -{ "description": "Open a confirmation, then open a menu over that, then close the menu. Verify that the confirmation popup also closes automatically", "speed": 20 } -``` - -The `speed` key refers to the playback speed as a multiple of the original recording speed. So 20 means the test will run 20 times faster than the original recording speed. If a test fails for a given speed, it will drop the speed and re-test, until finally attempting the test at the original speed. If you omit the speed, it will default to 10. - -### `setup.sh` - -This is a bash script containing the instructions for creating the test repo from scratch. For example: - -``` -#!/bin/sh - -cd $1 - -git init - -git config user.email "CI@example.com" -git config user.name "CI" - -echo test1 > myfile1 -git add . -git commit -am "myfile1" -``` - -Be sure to: - -- ensure that by the end of the test you've got at least one commit in the repo, as we've had issues in the past when that wasn't the case. -- set the git user email and name as above so that your own user details aren't included in the snapshot. - -## Running tests - -### From a TUI - -You can run/record/sandbox tests via a TUI with the following command: - -``` -go run test/lazyintegration/main.go -``` - -This TUI makes much of the following documentation redundant, but feel free to read through anyway! - -### From command line - -To run all tests - assuming you're at the project root: - -``` -go test ./pkg/gui/ -``` - -To run them in parallel - -``` -PARALLEL=true go test ./pkg/gui -``` - -To run a single test - -``` -go test ./pkg/gui -run / -# For example, to run the `tags` test: -go test ./pkg/gui -run /tags -``` - -To run a test at a certain speed - -``` -SPEED=2 go test ./pkg/gui -run / -``` - -To update a snapshot - -``` -MODE=updateSnapshot go test ./pkg/gui -run / -``` - -## Creating a new test - -To create a new test: - -1. Copy and paste an existing test directory and rename the new directory to whatever you want the test name to be. Update the test.json file's description to describe your test. -2. Update the `setup.sh` any way you like -3. If you want to have a config folder for just that test, create a `config` directory to contain a `config.yml` and optionally a `state.yml` file. Otherwise, the `test/default_test_config` directory will be used. -4. From the lazygit root directory, run: - -``` -MODE=record go test ./pkg/gui -run / -``` - -5. Feel free to re-attempt recording as many times as you like. In the absence of a proper testing framework, the more deliberate your keypresses, the better! -6. Once satisfied with the recording, stage all the newly created files: `test.json`, `setup.sh`, `recording.json` and the `expected` directory that contains a copy of the repo you created. - -The resulting directory will look like: - -``` -actual/ (the resulting repo(s) after running the test, ignored by git) -expected/ (the 'snapshot' repo(s)) -config/ (need not be present) -test.json -setup.sh -recording.json -``` - -## Sandboxing - -The integration tests serve a secondary purpose of providing a setup for easy sandboxing. If you want to run a test in sandbox mode (meaning the session won't be recorded and we won't create/update snapshots), go: - -``` -MODE=sandbox go test ./pkg/gui -run / -``` - -## Feedback - -If you think this process can be improved, let me know! It shouldn't be too hard to change things. +see pkg/integration/README.md diff --git a/pkg/integration/README.md b/pkg/integration/README.md index e8df3f190..c38bef931 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -1,8 +1,6 @@ # Integration Tests -There's a lot happening in this package so it's worth a proper explanation. - -This package is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected. +The pkg/integration pacakge is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected. ## Writing tests @@ -43,19 +41,19 @@ There are three ways to invoke a test: 2. go run pkg/integration/tui/main.go 3. go test pkg/integration/integration_test.go -The first, the test runner, is for directly running a test from the command line. +The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests. The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output. -The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. +The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run pkg/integration/runner/main.go commit/new_branch`. -You can pass the KEY_PRESS_DELAY env var to the first command in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. +You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay. -## Snapshots +### Snapshots -At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`. Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=updateSnapshot to the test runner or the go test command. This is useful when you've made a change to +At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`, in folders named 'expected' (alongside the 'actual' folders which contain the resulting repo from the last test run). Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=updateSnapshot to the test runner or the go test command. This is useful when you've made a change to -## Sandbox mode +### Sandbox mode Say you want to do a manual test of how lazygit handles merge-conflicts, but you can't be bothered actually finding a way to create merge conflicts in a repo. To make your life easier, you can simply run a merge-conflicts test in sandbox mode, meaning the setup step is run for you, and then instead of the test driving the lazygit session, you're allowed to drive it yourself. @@ -72,3 +70,5 @@ We should never write any new tests under the old method, and if a given test br ``` go run pkg/integration/deprecated/tui/main.go ``` + +The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test. diff --git a/pkg/integration/deprecated/integration.go b/pkg/integration/deprecated/integration.go index 761c978e0..5f597fc81 100644 --- a/pkg/integration/deprecated/integration.go +++ b/pkg/integration/deprecated/integration.go @@ -18,9 +18,9 @@ import ( "github.com/jesseduffield/lazygit/pkg/secureexec" ) -// This package is for running our integration test suite. See docs/Integration_Tests.md for more info. +// Deprecated: This file is part of the old way of doing things. See pkg/integration/integration.go for the new way -// Deprecated: This file is part of the old way of doing things. See integration.go for the new way +// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.mdfor more info. type Test struct { Name string `json:"name"` diff --git a/pkg/integration/deprecated/runner/main.go b/pkg/integration/deprecated/runner/main.go index 18398a450..904cdbeab 100644 --- a/pkg/integration/deprecated/runner/main.go +++ b/pkg/integration/deprecated/runner/main.go @@ -13,7 +13,7 @@ import ( // Deprecated: This file is part of the old way of doing things. See pkg/integration/runner/main.go for the new way -// see docs/Integration_Tests.md +// see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md // This file can be invoked directly, but you might find it easier to go through // test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. // diff --git a/pkg/integration/deprecated/tui/main.go b/pkg/integration/deprecated/tui/main.go index b455c53bd..9830f4229 100644 --- a/pkg/integration/deprecated/tui/main.go +++ b/pkg/integration/deprecated/tui/main.go @@ -16,7 +16,7 @@ import ( // Deprecated. See lazy_integration for the new approach. -// this program lets you manage integration tests in a TUI. See docs/Integration_Tests.md for more info. +// this program lets you manage integration tests in a TUI. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info. type App struct { tests []*deprecated.Test diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index 217ca9cb0..ed30c63b8 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -10,13 +10,9 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) -// see docs/Integration_Tests.md -// This file can be invoked directly, but you might find it easier to go through -// test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests. -// -// If invoked directly, you can specify a test by passing it as the first argument. -// You can also specify that you want to record a test by passing MODE=record -// as an env var. +// see pkg/integration/README.md + +// If invoked directly, you can specify tests to run by passing them as positional arguments. func main() { mode := integration.GetModeFromEnv() diff --git a/pkg/integration/tui/main.go b/pkg/integration/tui/main.go index 023c764b5..af118918b 100644 --- a/pkg/integration/tui/main.go +++ b/pkg/integration/tui/main.go @@ -15,7 +15,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/secureexec" ) -// this program lets you manage integration tests in a TUI. See docs/Integration_Tests.md for more info. +// this program lets you manage integration tests in a TUI. See pkg/integration/README.md for more info. type App struct { tests []*helpers.Test diff --git a/scripts/bisect.sh b/scripts/bisect.sh index 0e5f404cb..a3bc5f19e 100755 --- a/scripts/bisect.sh +++ b/scripts/bisect.sh @@ -2,7 +2,7 @@ # How to use: # 1) find a commit that is working fine. -# 2) Create an integration test capturing the fact that it works (Don't commit it). See https://github.com/jesseduffield/lazygit/blob/master/docs/Integration_Tests.md +# 2) Create an integration test capturing the fact that it works (Don't commit it). See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md # 3) checkout the commit that's known to be failing # 4) run this script supplying the commit sha / tag name that works and the name of the newly created test From 1ef6f4c0e1f71d1285b9892b94c6e519d2739521 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 11 Aug 2022 21:21:46 +1000 Subject: [PATCH 08/16] renaming --- docs/Integration_Tests.md | 2 +- pkg/integration/integration.go | 10 +++++----- pkg/integration/integration_test.go | 2 +- pkg/integration/runner/main.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Integration_Tests.md b/docs/Integration_Tests.md index bbc2ebe9f..fab7bb984 100644 --- a/docs/Integration_Tests.md +++ b/docs/Integration_Tests.md @@ -1 +1 @@ -see pkg/integration/README.md +see new docs [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index cb2b79861..b479614c9 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -42,7 +42,7 @@ type ( logf func(format string, formatArgs ...interface{}) ) -func RunTestsNew( +func RunTests( logf logf, runCmd func(cmd *exec.Cmd) error, fnWrapper func(test *helpers.Test, f func() error), @@ -82,14 +82,14 @@ func RunTestsNew( findOrCreateDir(testPath) prepareIntegrationTestDir(actualDir) findOrCreateDir(actualRepoDir) - err := createFixtureNew(test, actualRepoDir, rootDir) + err := createFixture(test, actualRepoDir, rootDir) if err != nil { return err } configDir := filepath.Join(testPath, "used_config") - cmd, err := getLazygitCommandNew(test, testPath, rootDir) + cmd, err := getLazygitCommand(test, testPath, rootDir) if err != nil { return err } @@ -229,7 +229,7 @@ func compareSnapshots(logf logf, configDir string, actualDir string, expectedDir return nil } -func createFixtureNew(test *helpers.Test, actualDir string, rootDir string) error { +func createFixture(test *helpers.Test, actualDir string, rootDir string) error { if err := os.Chdir(actualDir); err != nil { panic(err) } @@ -249,7 +249,7 @@ func createFixtureNew(test *helpers.Test, actualDir string, rootDir string) erro return nil } -func getLazygitCommandNew(test *helpers.Test, testPath string, rootDir string) (*exec.Cmd, error) { +func getLazygitCommand(test *helpers.Test, testPath string, rootDir string) (*exec.Cmd, error) { osCommand := oscommands.NewDummyOSCommand() templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index 3e9da8fe9..98001ac55 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -31,7 +31,7 @@ func TestIntegration(t *testing.T) { parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0) testNumber := 0 - err := RunTestsNew( + err := RunTests( t.Logf, runCmdHeadless, func(test *helpers.Test, f func() error) { diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index ed30c63b8..739474f96 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -39,7 +39,7 @@ func main() { return test.Name() }) - err := integration.RunTestsNew( + err := integration.RunTests( log.Printf, runCmdInTerminal, func(test *helpers.Test, f func() error) { From 610eddfe05cf68861bcfa1566d0d2d43aacab3fc Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 11 Aug 2022 21:28:55 +1000 Subject: [PATCH 09/16] fix CI --- .github/workflows/ci.yml | 30 +++++++++++++++++-- pkg/integration/deprecated/integration.go | 10 +++---- .../deprecated/integration_test.go | 6 ++-- pkg/integration/deprecated/runner/main.go | 2 +- pkg/integration/deprecated/tui/main.go | 4 +-- pkg/integration/helpers/test.go | 24 +++++++-------- pkg/integration/integration.go | 6 ++-- pkg/integration/integration_test.go | 2 +- pkg/integration/runner/main.go | 6 ++-- pkg/integration/tests/branch/suggestions.go | 2 +- pkg/integration/tests/commit/commit.go | 2 +- pkg/integration/tests/commit/new_branch.go | 2 +- .../tests/interactive_rebase/one.go | 2 +- pkg/integration/tests/tests.go | 2 +- pkg/integration/tui/main.go | 4 +-- 15 files changed, 64 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bf5ecfdf..c0b695e5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,14 +46,14 @@ jobs: # we're passing -short so that we skip the integration tests, which will be run in parallel below run: | go test ./... -short - integration-tests: + integration-tests-old: runs-on: ubuntu-latest strategy: fail-fast: false matrix: parallelism: [5] index: [0,1,2,3,4] - name: "Integration Tests (${{ matrix.index }}/${{ matrix.parallelism }})" + name: "Integration Tests (Old pattern) (${{ matrix.index }}/${{ matrix.parallelism }})" env: GOFLAGS: -mod=vendor steps: @@ -74,7 +74,31 @@ jobs: ${{runner.os}}-go- - name: Test code run: | - PARALLEL_TOTAL=${{ matrix.parallelism }} PARALLEL_INDEX=${{ matrix.index }} go test pkg/gui/gui_test.go + PARALLEL_TOTAL=${{ matrix.parallelism }} PARALLEL_INDEX=${{ matrix.index }} go test pkg/integration/deprecated/*.go + integration-tests: + runs-on: ubuntu-latest + name: "Integration Tests" + env: + GOFLAGS: -mod=vendor + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v1 + with: + go-version: 1.18.x + - name: Cache build + uses: actions/cache@v1 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test + restore-keys: | + ${{runner.os}}-go- + - name: Test code + run: | + go test pkg/integration/*.go build: runs-on: ubuntu-latest env: diff --git a/pkg/integration/deprecated/integration.go b/pkg/integration/deprecated/integration.go index 5f597fc81..92528b8ad 100644 --- a/pkg/integration/deprecated/integration.go +++ b/pkg/integration/deprecated/integration.go @@ -22,7 +22,7 @@ import ( // This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.mdfor more info. -type Test struct { +type IntegrationTest struct { Name string `json:"name"` Speed float64 `json:"speed"` Description string `json:"description"` @@ -66,7 +66,7 @@ func GetModeFromEnv() Mode { func RunTests( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test *Test, f func(*testing.T) error), + fnWrapper func(test *IntegrationTest, f func(*testing.T) error), mode Mode, speedEnv string, onFail func(t *testing.T, expected string, actual string, prefix string), @@ -315,13 +315,13 @@ func getTestSpeeds(testStartSpeed float64, mode Mode, speedStr string) []float64 return speeds } -func LoadTests(testDir string) ([]*Test, error) { +func LoadTests(testDir string) ([]*IntegrationTest, error) { paths, err := filepath.Glob(filepath.Join(testDir, "/*/test.json")) if err != nil { return nil, err } - tests := make([]*Test, len(paths)) + tests := make([]*IntegrationTest, len(paths)) for i, path := range paths { data, err := ioutil.ReadFile(path) @@ -329,7 +329,7 @@ func LoadTests(testDir string) ([]*Test, error) { return nil, err } - test := &Test{} + test := &IntegrationTest{} err = json.Unmarshal(data, test) if err != nil { diff --git a/pkg/integration/deprecated/integration_test.go b/pkg/integration/deprecated/integration_test.go index e66cc861a..fbec34bd9 100644 --- a/pkg/integration/deprecated/integration_test.go +++ b/pkg/integration/deprecated/integration_test.go @@ -16,7 +16,7 @@ import ( "github.com/stretchr/testify/assert" ) -// Deprecated: this is the old way of running tests. See pkg/gui/gui_test.go for the new way. +// Deprecated. // This file is quite similar to integration/main.go. The main difference is that this file is // run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides @@ -40,7 +40,7 @@ import ( // trying at the original playback speed (speed 1). A speed of 2 represents twice the // original playback speed. Speed may be a decimal. -func TestOld(t *testing.T) { +func Test(t *testing.T) { if testing.Short() { t.Skip("Skipping integration tests in short mode") } @@ -56,7 +56,7 @@ func TestOld(t *testing.T) { err := RunTests( t.Logf, runCmdHeadless, - func(test *Test, f func(*testing.T) error) { + func(test *IntegrationTest, f func(*testing.T) error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return diff --git a/pkg/integration/deprecated/runner/main.go b/pkg/integration/deprecated/runner/main.go index 904cdbeab..82e4bb0a9 100644 --- a/pkg/integration/deprecated/runner/main.go +++ b/pkg/integration/deprecated/runner/main.go @@ -30,7 +30,7 @@ func main() { err := deprecated.RunTests( log.Printf, runCmdInTerminal, - func(test *deprecated.Test, f func(*testing.T) error) { + func(test *deprecated.IntegrationTest, f func(*testing.T) error) { if selectedTestName != "" && test.Name != selectedTestName { return } diff --git a/pkg/integration/deprecated/tui/main.go b/pkg/integration/deprecated/tui/main.go index 9830f4229..80580ccff 100644 --- a/pkg/integration/deprecated/tui/main.go +++ b/pkg/integration/deprecated/tui/main.go @@ -19,14 +19,14 @@ import ( // this program lets you manage integration tests in a TUI. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info. type App struct { - tests []*deprecated.Test + tests []*deprecated.IntegrationTest itemIdx int testDir string editing bool g *gocui.Gui } -func (app *App) getCurrentTest() *deprecated.Test { +func (app *App) getCurrentTest() *deprecated.IntegrationTest { if len(app.tests) > 0 { return app.tests[app.itemIdx] } diff --git a/pkg/integration/helpers/test.go b/pkg/integration/helpers/test.go index 972bb1d5b..fb876be7f 100644 --- a/pkg/integration/helpers/test.go +++ b/pkg/integration/helpers/test.go @@ -12,7 +12,7 @@ import ( // Test describes an integration tests that will be run against the lazygit gui. -type Test struct { +type IntegrationTest struct { name string description string extraCmdArgs string @@ -27,9 +27,9 @@ type Test struct { ) } -var _ integrationTypes.IntegrationTest = &Test{} +var _ integrationTypes.IntegrationTest = &IntegrationTest{} -type NewTestArgs struct { +type NewIntegrationTestArgs struct { // Briefly describes what happens in the test and what it's testing for Description string // prepares a repo for testing @@ -44,8 +44,8 @@ type NewTestArgs struct { Skip bool } -func NewTest(args NewTestArgs) *Test { - return &Test{ +func NewIntegrationTest(args NewIntegrationTestArgs) *IntegrationTest { + return &IntegrationTest{ name: testNameFromFilePath(), description: args.Description, extraCmdArgs: args.ExtraCmdArgs, @@ -56,32 +56,32 @@ func NewTest(args NewTestArgs) *Test { } } -func (self *Test) Name() string { +func (self *IntegrationTest) Name() string { return self.name } -func (self *Test) Description() string { +func (self *IntegrationTest) Description() string { return self.description } -func (self *Test) ExtraCmdArgs() string { +func (self *IntegrationTest) ExtraCmdArgs() string { return self.extraCmdArgs } -func (self *Test) Skip() bool { +func (self *IntegrationTest) Skip() bool { return self.skip } -func (self *Test) SetupConfig(config *config.AppConfig) { +func (self *IntegrationTest) SetupConfig(config *config.AppConfig) { self.setupConfig(config) } -func (self *Test) SetupRepo(shell *Shell) { +func (self *IntegrationTest) SetupRepo(shell *Shell) { self.setupRepo(shell) } // I want access to all contexts, the model, the ability to press a key, the ability to log, -func (self *Test) Run(gui integrationTypes.GuiAdapter) { +func (self *IntegrationTest) Run(gui integrationTypes.GuiAdapter) { shell := NewShell() assert := NewAssert(gui) keys := gui.Keys() diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index b479614c9..6227649e6 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -45,7 +45,7 @@ type ( func RunTests( logf logf, runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test *helpers.Test, f func() error), + fnWrapper func(test *helpers.IntegrationTest, f func() error), mode Mode, includeSkipped bool, ) error { @@ -229,7 +229,7 @@ func compareSnapshots(logf logf, configDir string, actualDir string, expectedDir return nil } -func createFixture(test *helpers.Test, actualDir string, rootDir string) error { +func createFixture(test *helpers.IntegrationTest, actualDir string, rootDir string) error { if err := os.Chdir(actualDir); err != nil { panic(err) } @@ -249,7 +249,7 @@ func createFixture(test *helpers.Test, actualDir string, rootDir string) error { return nil } -func getLazygitCommand(test *helpers.Test, testPath string, rootDir string) (*exec.Cmd, error) { +func getLazygitCommand(test *helpers.IntegrationTest, testPath string, rootDir string) (*exec.Cmd, error) { osCommand := oscommands.NewDummyOSCommand() templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index 98001ac55..2c59b94df 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -34,7 +34,7 @@ func TestIntegration(t *testing.T) { err := RunTests( t.Logf, runCmdHeadless, - func(test *helpers.Test, f func() error) { + func(test *helpers.IntegrationTest, f func() error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index 739474f96..820df712a 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -17,7 +17,7 @@ import ( func main() { mode := integration.GetModeFromEnv() includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" - var testsToRun []*helpers.Test + var testsToRun []*helpers.IntegrationTest if len(os.Args) > 1 { outer: @@ -35,14 +35,14 @@ func main() { testsToRun = integration.Tests } - testNames := slices.Map(testsToRun, func(test *helpers.Test) string { + testNames := slices.Map(testsToRun, func(test *helpers.IntegrationTest) string { return test.Name() }) err := integration.RunTests( log.Printf, runCmdInTerminal, - func(test *helpers.Test, f func() error) { + func(test *helpers.IntegrationTest, f func() error) { if !slices.Contains(testNames, test.Name()) { return } diff --git a/pkg/integration/tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go index f4253cf82..33d0cf316 100644 --- a/pkg/integration/tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -5,7 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) -var Suggestions = helpers.NewTest(helpers.NewTestArgs{ +var Suggestions = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ Description: "Checking out a branch with name suggestions", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/tests/commit/commit.go b/pkg/integration/tests/commit/commit.go index 8aa0501e5..bbdcf5278 100644 --- a/pkg/integration/tests/commit/commit.go +++ b/pkg/integration/tests/commit/commit.go @@ -5,7 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) -var Commit = helpers.NewTest(helpers.NewTestArgs{ +var Commit = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ Description: "Staging a couple files and committing", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/tests/commit/new_branch.go b/pkg/integration/tests/commit/new_branch.go index f3ba36e2d..419da6890 100644 --- a/pkg/integration/tests/commit/new_branch.go +++ b/pkg/integration/tests/commit/new_branch.go @@ -5,7 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) -var NewBranch = helpers.NewTest(helpers.NewTestArgs{ +var NewBranch = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ Description: "Creating a new branch from a commit", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/tests/interactive_rebase/one.go b/pkg/integration/tests/interactive_rebase/one.go index 2b2acce37..69332c7de 100644 --- a/pkg/integration/tests/interactive_rebase/one.go +++ b/pkg/integration/tests/interactive_rebase/one.go @@ -5,7 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) -var One = helpers.NewTest(helpers.NewTestArgs{ +var One = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", ExtraCmdArgs: "", Skip: false, diff --git a/pkg/integration/tests/tests.go b/pkg/integration/tests/tests.go index c2e360e43..dbd44470d 100644 --- a/pkg/integration/tests/tests.go +++ b/pkg/integration/tests/tests.go @@ -10,7 +10,7 @@ import ( // Here is where we lists the actual tests that will run. When you create a new test, // be sure to add it to this list. -var Tests = []*helpers.Test{ +var Tests = []*helpers.IntegrationTest{ commit.Commit, commit.NewBranch, branch.Suggestions, diff --git a/pkg/integration/tui/main.go b/pkg/integration/tui/main.go index af118918b..8b00c4951 100644 --- a/pkg/integration/tui/main.go +++ b/pkg/integration/tui/main.go @@ -18,14 +18,14 @@ import ( // this program lets you manage integration tests in a TUI. See pkg/integration/README.md for more info. type App struct { - tests []*helpers.Test + tests []*helpers.IntegrationTest itemIdx int testDir string filtering bool g *gocui.Gui } -func (app *App) getCurrentTest() *helpers.Test { +func (app *App) getCurrentTest() *helpers.IntegrationTest { if len(app.tests) > 0 { return app.tests[app.itemIdx] } From de84b6c4b94c3b97be97b1ccccf653cd366fb8b4 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Thu, 11 Aug 2022 21:30:26 +1000 Subject: [PATCH 10/16] remove buggy-ass action --- .github/workflows/automerge.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml deleted file mode 100644 index 36d301e59..000000000 --- a/.github/workflows/automerge.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: automerge -on: - pull_request: - types: - - labeled - - unlabeled - - synchronize - - opened - - edited - - ready_for_review - - reopened - - unlocked - pull_request_review: - types: - - submitted - check_suite: - types: - - completed - status: {} -jobs: - automerge: - runs-on: ubuntu-latest - steps: - - name: automerge - uses: "pascalgn/automerge-action@135f0bdb927d9807b5446f7ca9ecc2c51de03c4a" - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - MERGE_METHOD: rebase From b8d944399901685e5edc1bded23457db0dd283c5 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Aug 2022 09:19:39 +1000 Subject: [PATCH 11/16] rename helpers to components --- pkg/integration/{helpers => components}/assert.go | 2 +- pkg/integration/{helpers => components}/input.go | 2 +- pkg/integration/{helpers => components}/shell.go | 2 +- pkg/integration/{helpers => components}/test.go | 2 +- pkg/integration/integration.go | 10 +++++----- pkg/integration/integration_test.go | 3 +-- pkg/integration/runner/main.go | 7 +++---- pkg/integration/tests/branch/suggestions.go | 8 ++++---- pkg/integration/tests/commit/commit.go | 8 ++++---- pkg/integration/tests/commit/new_branch.go | 8 ++++---- pkg/integration/tests/interactive_rebase/one.go | 8 ++++---- pkg/integration/tests/tests.go | 4 ++-- pkg/integration/tui/main.go | 5 ++--- 13 files changed, 33 insertions(+), 36 deletions(-) rename pkg/integration/{helpers => components}/assert.go (99%) rename pkg/integration/{helpers => components}/input.go (99%) rename pkg/integration/{helpers => components}/shell.go (99%) rename pkg/integration/{helpers => components}/test.go (99%) diff --git a/pkg/integration/helpers/assert.go b/pkg/integration/components/assert.go similarity index 99% rename from pkg/integration/helpers/assert.go rename to pkg/integration/components/assert.go index 41b280bcb..11048532d 100644 --- a/pkg/integration/helpers/assert.go +++ b/pkg/integration/components/assert.go @@ -1,4 +1,4 @@ -package helpers +package components import ( "fmt" diff --git a/pkg/integration/helpers/input.go b/pkg/integration/components/input.go similarity index 99% rename from pkg/integration/helpers/input.go rename to pkg/integration/components/input.go index 55e1f8527..bf52e554c 100644 --- a/pkg/integration/helpers/input.go +++ b/pkg/integration/components/input.go @@ -1,4 +1,4 @@ -package helpers +package components import ( "fmt" diff --git a/pkg/integration/helpers/shell.go b/pkg/integration/components/shell.go similarity index 99% rename from pkg/integration/helpers/shell.go rename to pkg/integration/components/shell.go index b70a8ffaa..ee57cf401 100644 --- a/pkg/integration/helpers/shell.go +++ b/pkg/integration/components/shell.go @@ -1,4 +1,4 @@ -package helpers +package components import ( "fmt" diff --git a/pkg/integration/helpers/test.go b/pkg/integration/components/test.go similarity index 99% rename from pkg/integration/helpers/test.go rename to pkg/integration/components/test.go index fb876be7f..13ffe73f1 100644 --- a/pkg/integration/helpers/test.go +++ b/pkg/integration/components/test.go @@ -1,4 +1,4 @@ -package helpers +package components import ( "os" diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 6227649e6..c50594054 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -12,7 +12,7 @@ import ( "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/tests" "github.com/stretchr/testify/assert" ) @@ -45,7 +45,7 @@ type ( func RunTests( logf logf, runCmd func(cmd *exec.Cmd) error, - fnWrapper func(test *helpers.IntegrationTest, f func() error), + fnWrapper func(test *components.IntegrationTest, f func() error), mode Mode, includeSkipped bool, ) error { @@ -229,12 +229,12 @@ func compareSnapshots(logf logf, configDir string, actualDir string, expectedDir return nil } -func createFixture(test *helpers.IntegrationTest, actualDir string, rootDir string) error { +func createFixture(test *components.IntegrationTest, actualDir string, rootDir string) error { if err := os.Chdir(actualDir); err != nil { panic(err) } - shell := helpers.NewShell() + shell := components.NewShell() shell.RunCommand("git init") shell.RunCommand(`git config user.email "CI@example.com"`) shell.RunCommand(`git config user.name "CI"`) @@ -249,7 +249,7 @@ func createFixture(test *helpers.IntegrationTest, actualDir string, rootDir stri return nil } -func getLazygitCommand(test *helpers.IntegrationTest, testPath string, rootDir string) (*exec.Cmd, error) { +func getLazygitCommand(test *components.IntegrationTest, testPath string, rootDir string) (*exec.Cmd, error) { osCommand := oscommands.NewDummyOSCommand() templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index 2c59b94df..3637b0a70 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -15,7 +15,6 @@ import ( "testing" "github.com/creack/pty" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/stretchr/testify/assert" ) @@ -34,7 +33,7 @@ func TestIntegration(t *testing.T) { err := RunTests( t.Logf, runCmdHeadless, - func(test *helpers.IntegrationTest, f func() error) { + func(test *components.IntegrationTest, f func() error) { defer func() { testNumber += 1 }() if testNumber%parallelTotal != parallelIndex { return diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index 820df712a..25c0acf8f 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -7,7 +7,6 @@ import ( "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" ) // see pkg/integration/README.md @@ -17,7 +16,7 @@ import ( func main() { mode := integration.GetModeFromEnv() includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true" - var testsToRun []*helpers.IntegrationTest + var testsToRun []*components.IntegrationTest if len(os.Args) > 1 { outer: @@ -35,14 +34,14 @@ func main() { testsToRun = integration.Tests } - testNames := slices.Map(testsToRun, func(test *helpers.IntegrationTest) string { + testNames := slices.Map(testsToRun, func(test *components.IntegrationTest) string { return test.Name() }) err := integration.RunTests( log.Printf, runCmdInTerminal, - func(test *helpers.IntegrationTest, f func() error) { + func(test *components.IntegrationTest, f func() error) { if !slices.Contains(testNames, test.Name()) { return } diff --git a/pkg/integration/tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go index 33d0cf316..2bd4fa340 100644 --- a/pkg/integration/tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -2,15 +2,15 @@ package branch import ( "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" ) -var Suggestions = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ +var Suggestions = components.NewIntegrationTest(components.NewIntegrationTestArgs{ Description: "Checking out a branch with name suggestions", ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell *helpers.Shell) { + SetupRepo: func(shell *components.Shell) { shell. EmptyCommit("my commit message"). NewBranch("new-branch"). @@ -20,7 +20,7 @@ var Suggestions = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ NewBranch("other-new-branch-2"). NewBranch("other-new-branch-3") }, - Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() input.PressKeys(keys.Branches.CheckoutBranchByName) diff --git a/pkg/integration/tests/commit/commit.go b/pkg/integration/tests/commit/commit.go index bbdcf5278..12a68925d 100644 --- a/pkg/integration/tests/commit/commit.go +++ b/pkg/integration/tests/commit/commit.go @@ -2,19 +2,19 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" ) -var Commit = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ +var Commit = components.NewIntegrationTest(components.NewIntegrationTestArgs{ Description: "Staging a couple files and committing", ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell *helpers.Shell) { + SetupRepo: func(shell *components.Shell) { shell.CreateFile("myfile", "myfile content") shell.CreateFile("myfile2", "myfile2 content") }, - Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { assert.CommitCount(0) input.Select() diff --git a/pkg/integration/tests/commit/new_branch.go b/pkg/integration/tests/commit/new_branch.go index 419da6890..ad96938f5 100644 --- a/pkg/integration/tests/commit/new_branch.go +++ b/pkg/integration/tests/commit/new_branch.go @@ -2,21 +2,21 @@ package commit import ( "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" ) -var NewBranch = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ +var NewBranch = components.NewIntegrationTest(components.NewIntegrationTestArgs{ Description: "Creating a new branch from a commit", ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell *helpers.Shell) { + SetupRepo: func(shell *components.Shell) { shell. EmptyCommit("commit 1"). EmptyCommit("commit 2"). EmptyCommit("commit 3") }, - Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { assert.CommitCount(3) input.SwitchToCommitsWindow() diff --git a/pkg/integration/tests/interactive_rebase/one.go b/pkg/integration/tests/interactive_rebase/one.go index 69332c7de..3c785a727 100644 --- a/pkg/integration/tests/interactive_rebase/one.go +++ b/pkg/integration/tests/interactive_rebase/one.go @@ -2,19 +2,19 @@ package interactive_rebase import ( "github.com/jesseduffield/lazygit/pkg/config" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" ) -var One = helpers.NewIntegrationTest(helpers.NewIntegrationTestArgs{ +var One = components.NewIntegrationTest(components.NewIntegrationTestArgs{ Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", ExtraCmdArgs: "", Skip: false, SetupConfig: func(config *config.AppConfig) {}, - SetupRepo: func(shell *helpers.Shell) { + SetupRepo: func(shell *components.Shell) { shell. CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 }, - Run: func(shell *helpers.Shell, input *helpers.Input, assert *helpers.Assert, keys config.KeybindingConfig) { + Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { input.SwitchToCommitsWindow() assert.CurrentViewName("commits") diff --git a/pkg/integration/tests/tests.go b/pkg/integration/tests/tests.go index dbd44470d..e9794169a 100644 --- a/pkg/integration/tests/tests.go +++ b/pkg/integration/tests/tests.go @@ -1,7 +1,7 @@ package tests import ( - "github.com/jesseduffield/lazygit/pkg/integration/helpers" + "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/tests/branch" "github.com/jesseduffield/lazygit/pkg/integration/tests/commit" "github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase" @@ -10,7 +10,7 @@ import ( // Here is where we lists the actual tests that will run. When you create a new test, // be sure to add it to this list. -var Tests = []*helpers.IntegrationTest{ +var Tests = []*components.IntegrationTest{ commit.Commit, commit.NewBranch, branch.Suggestions, diff --git a/pkg/integration/tui/main.go b/pkg/integration/tui/main.go index 8b00c4951..97aa90c2e 100644 --- a/pkg/integration/tui/main.go +++ b/pkg/integration/tui/main.go @@ -11,21 +11,20 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/integration" - "github.com/jesseduffield/lazygit/pkg/integration/helpers" "github.com/jesseduffield/lazygit/pkg/secureexec" ) // this program lets you manage integration tests in a TUI. See pkg/integration/README.md for more info. type App struct { - tests []*helpers.IntegrationTest + tests []*components.IntegrationTest itemIdx int testDir string filtering bool g *gocui.Gui } -func (app *App) getCurrentTest() *helpers.IntegrationTest { +func (app *App) getCurrentTest() *components.IntegrationTest { if len(app.tests) > 0 { return app.tests[app.itemIdx] } From faed509bfd142a93cddf92a8795eaeb7e754b794 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Aug 2022 09:24:39 +1000 Subject: [PATCH 12/16] fix CI --- pkg/gui/services/custom_commands/resolver.go | 8 -------- pkg/integration/components/input.go | 6 +++--- pkg/integration/integration.go | 8 ++++---- pkg/integration/integration_test.go | 1 + pkg/integration/runner/main.go | 1 + pkg/integration/tui/main.go | 18 +----------------- 6 files changed, 10 insertions(+), 32 deletions(-) diff --git a/pkg/gui/services/custom_commands/resolver.go b/pkg/gui/services/custom_commands/resolver.go index 1b80987c1..4702d36c4 100644 --- a/pkg/gui/services/custom_commands/resolver.go +++ b/pkg/gui/services/custom_commands/resolver.go @@ -2,7 +2,6 @@ package custom_commands import ( "bytes" - "fmt" "text/template" "github.com/jesseduffield/lazygit/pkg/common" @@ -106,13 +105,6 @@ func (self *Resolver) resolveMenuOption(option *config.CustomCommandMenuOption, }, nil } -func main() { - fmt.Println(ResolveTemplate("old approach: {{index .PromptResponses 0}}, new approach: {{ .Form.a }}", CustomCommandObject{ - PromptResponses: []string{"a"}, - Form: map[string]string{"a": "B"}, - })) -} - type CustomCommandObject struct { // deprecated. Use Responses instead PromptResponses []string diff --git a/pkg/integration/components/input.go b/pkg/integration/components/input.go index bf52e554c..52d721ff9 100644 --- a/pkg/integration/components/input.go +++ b/pkg/integration/components/input.go @@ -117,9 +117,9 @@ func (self *Input) Log(message string) { // this will look for a list item in the current panel and if it finds it, it will // enter the keypresses required to navigate to it. // The test will fail if: -// - the user is not in a list item -// - no list item is found containing the given text -// - multiple list items are found containing the given text in the initial page of items +// - the user is not in a list item +// - no list item is found containing the given text +// - multiple list items are found containing the given text in the initial page of items // // NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. // If this changes in future, we'll need to update this code to first attempt to find the item diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index c50594054..f30101e26 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -101,7 +101,7 @@ func RunTests( switch mode { case UPDATE_SNAPSHOT: - if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + if err := updateSnapshot(actualDir, expectedDir); err != nil { return err } logf("Test passed: %s", test.Name()) @@ -112,7 +112,7 @@ func RunTests( logf("Test passed: %s", test.Name()) case ASK_TO_UPDATE_SNAPSHOT: if _, err := os.Stat(expectedDir); os.IsNotExist(err) { - if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + if err := updateSnapshot(actualDir, expectedDir); err != nil { return err } logf("No existing snapshot found for %s. Created snapshot.", test.Name()) @@ -125,7 +125,7 @@ func RunTests( // prompt user whether to update the snapshot (Y/N) if promptUserToUpdateSnapshot() { - if err := updateSnapshot(logf, actualDir, expectedDir); err != nil { + if err := updateSnapshot(actualDir, expectedDir); err != nil { return err } logf("Snapshot updated: %s", test.Name()) @@ -153,7 +153,7 @@ func promptUserToUpdateSnapshot() bool { return input == "y" } -func updateSnapshot(logf logf, actualDir string, expectedDir string) error { +func updateSnapshot(actualDir string, expectedDir string) error { // create/update snapshot err := oscommands.CopyDir(actualDir, expectedDir) if err != nil { diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index 3637b0a70..0e55f9886 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -15,6 +15,7 @@ import ( "testing" "github.com/creack/pty" + "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/stretchr/testify/assert" ) diff --git a/pkg/integration/runner/main.go b/pkg/integration/runner/main.go index 25c0acf8f..97c49440e 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/runner/main.go @@ -7,6 +7,7 @@ import ( "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/components" ) // see pkg/integration/README.md diff --git a/pkg/integration/tui/main.go b/pkg/integration/tui/main.go index 97aa90c2e..07f136f82 100644 --- a/pkg/integration/tui/main.go +++ b/pkg/integration/tui/main.go @@ -11,6 +11,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/secureexec" ) @@ -31,23 +32,6 @@ func (app *App) getCurrentTest() *components.IntegrationTest { return nil } -func (app *App) refreshTests() { - app.loadTests() - app.g.Update(func(*gocui.Gui) error { - listView, err := app.g.View("list") - if err != nil { - return err - } - - listView.Clear() - for _, test := range app.tests { - fmt.Fprintln(listView, test.Name()) - } - - return nil - }) -} - func (app *App) loadTests() { app.tests = integration.Tests if app.itemIdx > len(app.tests)-1 { From 2bdefe20498f58fe3e8e41a2be08650576ec6d6f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 12 Aug 2022 09:56:21 +1000 Subject: [PATCH 13/16] add assertion to prevent flakiness --- pkg/integration/tests/branch/suggestions.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/integration/tests/branch/suggestions.go b/pkg/integration/tests/branch/suggestions.go index 2bd4fa340..0d8269f1d 100644 --- a/pkg/integration/tests/branch/suggestions.go +++ b/pkg/integration/tests/branch/suggestions.go @@ -22,6 +22,7 @@ var Suggestions = components.NewIntegrationTest(components.NewIntegrationTestArg }, Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() + assert.CurrentViewName("localBranches") input.PressKeys(keys.Branches.CheckoutBranchByName) assert.CurrentViewName("confirmation") From 304d74370e720473ee384e171a8e8f00956fc72f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 13 Aug 2022 12:56:04 +1000 Subject: [PATCH 14/16] refactor to ensure code doesn't depend on integration code --- main.go | 135 +----------------- pkg/app/entry_point.go | 112 ++++++++++++++- pkg/gui/{gui_adapter.go => gui_driver.go} | 20 +-- pkg/gui/test_mode.go | 4 +- pkg/integration/README.md | 8 +- pkg/integration/cmd/injector/main.go | 47 ++++++ pkg/integration/{ => cmd}/runner/main.go | 8 +- pkg/integration/{ => cmd}/tui/main.go | 8 +- pkg/integration/components/assert.go | 4 +- pkg/integration/components/input.go | 4 +- pkg/integration/components/test.go | 2 +- .../deprecated/{ => cmd}/runner/main.go | 2 +- .../deprecated/{ => cmd}/tui/main.go | 10 +- pkg/integration/deprecated/integration.go | 2 +- pkg/integration/integration.go | 2 +- pkg/integration/types/types.go | 4 +- 16 files changed, 203 insertions(+), 169 deletions(-) rename pkg/gui/{gui_adapter.go => gui_driver.go} (73%) create mode 100644 pkg/integration/cmd/injector/main.go rename pkg/integration/{ => cmd}/runner/main.go (79%) rename pkg/integration/{ => cmd}/tui/main.go (94%) rename pkg/integration/deprecated/{ => cmd}/runner/main.go (96%) rename pkg/integration/deprecated/{ => cmd}/tui/main.go (96%) diff --git a/main.go b/main.go index 6faf41ddf..d7ce1db14 100644 --- a/main.go +++ b/main.go @@ -1,151 +1,24 @@ package main import ( - "os" - "runtime/debug" - - "github.com/integrii/flaggy" "github.com/jesseduffield/lazygit/pkg/app" - "github.com/jesseduffield/lazygit/pkg/integration" - integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" - "github.com/jesseduffield/lazygit/pkg/utils" - "github.com/samber/lo" ) -const DEFAULT_VERSION = "unversioned" - -// These values may be set by the build script. -// we'll overwrite them if they haven't been set by the build script and if Go itself has set corresponding values in the binary +// These values may be set by the build script via the LDFLAGS argument var ( commit string - version = DEFAULT_VERSION date string + version string buildSource = "unknown" ) func main() { - cliArgs := parseCliArgsAndEnvVars() - buildInfo := getBuildInfo() - integrationTest := getIntegrationTest() - - app.Start(cliArgs, buildInfo, integrationTest) -} - -func parseCliArgsAndEnvVars() *app.CliArgs { - flaggy.DefaultParser.ShowVersionWithVersionFlag = false - - repoPath := "" - flaggy.String(&repoPath, "p", "path", "Path of git repo. (equivalent to --work-tree= --git-dir=/.git/)") - - filterPath := "" - flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- `. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted") - - gitArg := "" - flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.") - - printVersionInfo := false - flaggy.Bool(&printVersionInfo, "v", "version", "Print the current version") - - debug := false - flaggy.Bool(&debug, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)") - - tailLogs := false - flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)") - - printDefaultConfig := false - flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config") - - printConfigDir := false - flaggy.Bool(&printConfigDir, "cd", "print-config-dir", "Print the config directory") - - useConfigDir := "" - flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory") - - workTree := "" - flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument") - - gitDir := "" - flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument") - - customConfigFile := "" - flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)") - - flaggy.Parse() - - if os.Getenv("DEBUG") == "TRUE" { - debug = true - } - - return &app.CliArgs{ - RepoPath: repoPath, - FilterPath: filterPath, - GitArg: gitArg, - PrintVersionInfo: printVersionInfo, - Debug: debug, - TailLogs: tailLogs, - PrintDefaultConfig: printDefaultConfig, - PrintConfigDir: printConfigDir, - UseConfigDir: useConfigDir, - WorkTree: workTree, - GitDir: gitDir, - CustomConfigFile: customConfigFile, - } -} - -func getBuildInfo() *app.BuildInfo { - buildInfo := &app.BuildInfo{ + ldFlagsBuildInfo := &app.BuildInfo{ Commit: commit, Date: date, Version: version, BuildSource: buildSource, } - // if the version has already been set by build flags then we'll honour that. - // chances are it's something like v0.31.0 which is more informative than a - // commit hash. - if buildInfo.Version != DEFAULT_VERSION { - return buildInfo - } - - goBuildInfo, ok := debug.ReadBuildInfo() - if !ok { - return buildInfo - } - - revision, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { - return setting.Key == "vcs.revision" - }) - if ok { - buildInfo.Commit = revision.Value - // if lazygit was built from source we'll show the version as the - // abbreviated commit hash - buildInfo.Version = utils.ShortSha(revision.Value) - } - - // if version hasn't been set we assume that neither has the date - time, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { - return setting.Key == "vcs.time" - }) - if ok { - buildInfo.Date = time.Value - } - - return buildInfo -} - -func getIntegrationTest() integrationTypes.IntegrationTest { - integrationTestName := os.Getenv("LAZYGIT_TEST_NAME") - if integrationTestName == "" { - return nil - } - - // unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again - os.Unsetenv("LAZYGIT_TEST_NAME") - for _, candidateTest := range integration.Tests { - if candidateTest.Name() == integrationTestName { - return candidateTest - } - } - - panic("Could not find integration test with name: " + integrationTestName) + app.Start(ldFlagsBuildInfo, nil) } diff --git a/pkg/app/entry_point.go b/pkg/app/entry_point.go index 551b959a6..5a767bb94 100644 --- a/pkg/app/entry_point.go +++ b/pkg/app/entry_point.go @@ -7,18 +7,22 @@ import ( "os" "path/filepath" "runtime" + "runtime/debug" "strings" + "github.com/integrii/flaggy" "github.com/jesseduffield/lazygit/pkg/app/daemon" appTypes "github.com/jesseduffield/lazygit/pkg/app/types" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/env" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" "github.com/jesseduffield/lazygit/pkg/logs" + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/samber/lo" "gopkg.in/yaml.v3" ) -type CliArgs struct { +type cliArgs struct { RepoPath string FilterPath string GitArg string @@ -40,7 +44,10 @@ type BuildInfo struct { BuildSource string } -func Start(cliArgs *CliArgs, buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTest) { +func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTest) { + cliArgs := parseCliArgsAndEnvVars() + mergeBuildInfo(buildInfo) + if cliArgs.RepoPath != "" { if cliArgs.WorkTree != "" || cliArgs.GitDir != "" { log.Fatal("--path option is incompatible with the --work-tree and --git-dir options") @@ -132,6 +139,67 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, integrationTest integrationTy Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest)) } +func parseCliArgsAndEnvVars() *cliArgs { + flaggy.DefaultParser.ShowVersionWithVersionFlag = false + + repoPath := "" + flaggy.String(&repoPath, "p", "path", "Path of git repo. (equivalent to --work-tree= --git-dir=/.git/)") + + filterPath := "" + flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- `. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted") + + gitArg := "" + flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.") + + printVersionInfo := false + flaggy.Bool(&printVersionInfo, "v", "version", "Print the current version") + + debug := false + flaggy.Bool(&debug, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)") + + tailLogs := false + flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)") + + printDefaultConfig := false + flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config") + + printConfigDir := false + flaggy.Bool(&printConfigDir, "cd", "print-config-dir", "Print the config directory") + + useConfigDir := "" + flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory") + + workTree := "" + flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument") + + gitDir := "" + flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument") + + customConfigFile := "" + flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)") + + flaggy.Parse() + + if os.Getenv("DEBUG") == "TRUE" { + debug = true + } + + return &cliArgs{ + RepoPath: repoPath, + FilterPath: filterPath, + GitArg: gitArg, + PrintVersionInfo: printVersionInfo, + Debug: debug, + TailLogs: tailLogs, + PrintDefaultConfig: printDefaultConfig, + PrintConfigDir: printConfigDir, + UseConfigDir: useConfigDir, + WorkTree: workTree, + GitDir: gitDir, + CustomConfigFile: customConfigFile, + } +} + func parseGitArg(gitArg string) appTypes.GitArg { typedArg := appTypes.GitArg(gitArg) @@ -155,3 +223,43 @@ func parseGitArg(gitArg string) appTypes.GitArg { panic("unreachable") } + +// the buildInfo struct we get passed in is based on what's baked into the lazygit +// binary via the LDFLAGS argument. Some lazygit distributions will make use of these +// arguments and some will not. Go recently started baking in build info +// into the binary by default e.g. the git commit hash. So in this function +// we merge the two together, giving priority to the stuff set by LDFLAGS. +// Note: this mutates the argument passed in +func mergeBuildInfo(buildInfo *BuildInfo) { + // if the version has already been set by build flags then we'll honour that. + // chances are it's something like v0.31.0 which is more informative than a + // commit hash. + if buildInfo.Version != "" { + return + } + + buildInfo.Version = "unversioned" + + goBuildInfo, ok := debug.ReadBuildInfo() + if !ok { + return + } + + revision, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { + return setting.Key == "vcs.revision" + }) + if ok { + buildInfo.Commit = revision.Value + // if lazygit was built from source we'll show the version as the + // abbreviated commit hash + buildInfo.Version = utils.ShortSha(revision.Value) + } + + // if version hasn't been set we assume that neither has the date + time, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool { + return setting.Key == "vcs.time" + }) + if ok { + buildInfo.Date = time.Value + } +} diff --git a/pkg/gui/gui_adapter.go b/pkg/gui/gui_driver.go similarity index 73% rename from pkg/gui/gui_adapter.go rename to pkg/gui/gui_driver.go index 5566f8b0f..860c6c9b8 100644 --- a/pkg/gui/gui_adapter.go +++ b/pkg/gui/gui_driver.go @@ -14,13 +14,13 @@ import ( // this gives our integration test a way of interacting with the gui for sending keypresses // and reading state. -type GuiAdapter struct { +type GuiDriver struct { gui *Gui } -var _ integrationTypes.GuiAdapter = &GuiAdapter{} +var _ integrationTypes.GuiDriver = &GuiDriver{} -func (self *GuiAdapter) PressKey(keyStr string) { +func (self *GuiDriver) PressKey(keyStr string) { key := keybindings.GetKey(keyStr) var r rune @@ -39,19 +39,19 @@ func (self *GuiAdapter) PressKey(keyStr string) { ) } -func (self *GuiAdapter) Keys() config.KeybindingConfig { +func (self *GuiDriver) Keys() config.KeybindingConfig { return self.gui.Config.GetUserConfig().Keybinding } -func (self *GuiAdapter) CurrentContext() types.Context { +func (self *GuiDriver) CurrentContext() types.Context { return self.gui.c.CurrentContext() } -func (self *GuiAdapter) Model() *types.Model { +func (self *GuiDriver) Model() *types.Model { return self.gui.State.Model } -func (self *GuiAdapter) Fail(message string) { +func (self *GuiDriver) Fail(message string) { self.gui.g.Close() // need to give the gui time to close time.Sleep(time.Millisecond * 100) @@ -59,15 +59,15 @@ func (self *GuiAdapter) Fail(message string) { } // logs to the normal place that you log to i.e. viewable with `lazygit --logs` -func (self *GuiAdapter) Log(message string) { +func (self *GuiDriver) Log(message string) { self.gui.c.Log.Warn(message) } // logs in the actual UI (in the commands panel) -func (self *GuiAdapter) LogUI(message string) { +func (self *GuiDriver) LogUI(message string) { self.gui.c.LogAction(message) } -func (self *GuiAdapter) CheckedOutRef() *models.Branch { +func (self *GuiDriver) CheckedOutRef() *models.Branch { return self.gui.helpers.Refs.GetCheckedOutRef() } diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index e9d596e80..cd3bd83ba 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -14,7 +14,7 @@ import ( ) type IntegrationTest interface { - Run(guiAdapter *GuiAdapter) + Run(guiAdapter *GuiDriver) } func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) { @@ -22,7 +22,7 @@ func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) { go func() { time.Sleep(time.Millisecond * 100) - test.Run(&GuiAdapter{gui: gui}) + test.Run(&GuiDriver{gui: gui}) gui.g.Update(func(*gocui.Gui) error { return gocui.ErrQuit diff --git a/pkg/integration/README.md b/pkg/integration/README.md index c38bef931..978a20600 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -37,15 +37,15 @@ If you find yourself doing something frequently in a test, consider making it a There are three ways to invoke a test: -1. go run pkg/integration/runner/main.go [...] -2. go run pkg/integration/tui/main.go +1. go run pkg/integration/cmd/runner/main.go [...] +2. go run pkg/integration/cmd/tui/main.go 3. go test pkg/integration/integration_test.go The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests. The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output. -The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run pkg/integration/runner/main.go commit/new_branch`. +The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run pkg/integration/cmd/runner/main.go commit/new_branch`. You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay. @@ -68,7 +68,7 @@ At the moment, all the deprecated test code lives in pkg/integration/deprecated. We should never write any new tests under the old method, and if a given test breaks because of new functionality, it's best to simply rewrite it under the new approach. If you want to run a test for the sake of watching what it does so that you can transcribe it into the new approach, you can run: ``` -go run pkg/integration/deprecated/tui/main.go +go run pkg/integration/deprecated/cmd/tui/main.go ``` The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test. diff --git a/pkg/integration/cmd/injector/main.go b/pkg/integration/cmd/injector/main.go new file mode 100644 index 000000000..9460c7b22 --- /dev/null +++ b/pkg/integration/cmd/injector/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "os" + + "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/lazygit/pkg/integration" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +// The purpose of this program is to run lazygit with an integration test passed in. +// We could have done the check on LAZYGIT_TEST_NAME in the root main.go but +// that would mean lazygit would be depending on integration test code which +// would bloat the binary. + +// You should not invoke this program directly. Instead you should go through +// pkg/integration/cmd/runner/main.go or pkg/integration/cmd/tui/main.go + +func main() { + dummyBuildInfo := &app.BuildInfo{ + Commit: "", + Date: "", + Version: "", + BuildSource: "integration test", + } + + integrationTest := getIntegrationTest() + + app.Start(dummyBuildInfo, integrationTest) +} + +func getIntegrationTest() integrationTypes.IntegrationTest { + integrationTestName := os.Getenv("LAZYGIT_TEST_NAME") + if integrationTestName == "" { + panic("expected LAZYGIT_TEST_NAME environment variable to be set, given that we're running an integration test") + } + + // unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again + os.Unsetenv("LAZYGIT_TEST_NAME") + for _, candidateTest := range integration.Tests { + if candidateTest.Name() == integrationTestName { + return candidateTest + } + } + + panic("Could not find integration test with name: " + integrationTestName) +} diff --git a/pkg/integration/runner/main.go b/pkg/integration/cmd/runner/main.go similarity index 79% rename from pkg/integration/runner/main.go rename to pkg/integration/cmd/runner/main.go index 97c49440e..1fbde96c5 100644 --- a/pkg/integration/runner/main.go +++ b/pkg/integration/cmd/runner/main.go @@ -12,7 +12,13 @@ import ( // see pkg/integration/README.md -// If invoked directly, you can specify tests to run by passing them as positional arguments. +// The purpose of this program is to run integration tests. It does this by +// building our injector program (in the sibling injector directory) and then for +// each test we're running, invoke the injector program with the test's name as +// an environment variable. Then the injector finds the test and passes it to +// the lazygit startup code. + +// If invoked directly, you can specify tests to run by passing their names as positional arguments func main() { mode := integration.GetModeFromEnv() diff --git a/pkg/integration/tui/main.go b/pkg/integration/cmd/tui/main.go similarity index 94% rename from pkg/integration/tui/main.go rename to pkg/integration/cmd/tui/main.go index 07f136f82..c9e533f61 100644 --- a/pkg/integration/tui/main.go +++ b/pkg/integration/cmd/tui/main.go @@ -15,7 +15,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/secureexec" ) -// this program lets you manage integration tests in a TUI. See pkg/integration/README.md for more info. +// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info. type App struct { tests []*components.IntegrationTest @@ -85,7 +85,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/runner/main.go %s", currentTest.Name())) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) app.runSubprocess(cmd) return nil @@ -99,7 +99,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/runner/main.go %s", currentTest.Name())) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) app.runSubprocess(cmd) return nil @@ -113,7 +113,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true KEY_PRESS_DELAY=200 go run pkg/integration/runner/main.go %s", currentTest.Name())) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true KEY_PRESS_DELAY=200 go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) app.runSubprocess(cmd) return nil diff --git a/pkg/integration/components/assert.go b/pkg/integration/components/assert.go index 11048532d..584ad438b 100644 --- a/pkg/integration/components/assert.go +++ b/pkg/integration/components/assert.go @@ -12,10 +12,10 @@ import ( // through this struct we assert on the state of the lazygit gui type Assert struct { - gui integrationTypes.GuiAdapter + gui integrationTypes.GuiDriver } -func NewAssert(gui integrationTypes.GuiAdapter) *Assert { +func NewAssert(gui integrationTypes.GuiDriver) *Assert { return &Assert{gui: gui} } diff --git a/pkg/integration/components/input.go b/pkg/integration/components/input.go index 52d721ff9..d44b11830 100644 --- a/pkg/integration/components/input.go +++ b/pkg/integration/components/input.go @@ -11,13 +11,13 @@ import ( ) type Input struct { - gui integrationTypes.GuiAdapter + gui integrationTypes.GuiDriver keys config.KeybindingConfig assert *Assert pushKeyDelay int } -func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input { +func NewInput(gui integrationTypes.GuiDriver, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input { return &Input{ gui: gui, keys: keys, diff --git a/pkg/integration/components/test.go b/pkg/integration/components/test.go index 13ffe73f1..2dc60c51f 100644 --- a/pkg/integration/components/test.go +++ b/pkg/integration/components/test.go @@ -81,7 +81,7 @@ func (self *IntegrationTest) SetupRepo(shell *Shell) { } // I want access to all contexts, the model, the ability to press a key, the ability to log, -func (self *IntegrationTest) Run(gui integrationTypes.GuiAdapter) { +func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) { shell := NewShell() assert := NewAssert(gui) keys := gui.Keys() diff --git a/pkg/integration/deprecated/runner/main.go b/pkg/integration/deprecated/cmd/runner/main.go similarity index 96% rename from pkg/integration/deprecated/runner/main.go rename to pkg/integration/deprecated/cmd/runner/main.go index 82e4bb0a9..e225b3bd3 100644 --- a/pkg/integration/deprecated/runner/main.go +++ b/pkg/integration/deprecated/cmd/runner/main.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -// Deprecated: This file is part of the old way of doing things. See pkg/integration/runner/main.go for the new way +// Deprecated: This file is part of the old way of doing things. See pkg/integration/cmd/runner/main.go for the new way // see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md // This file can be invoked directly, but you might find it easier to go through diff --git a/pkg/integration/deprecated/tui/main.go b/pkg/integration/deprecated/cmd/tui/main.go similarity index 96% rename from pkg/integration/deprecated/tui/main.go rename to pkg/integration/deprecated/cmd/tui/main.go index 80580ccff..136ed29fe 100644 --- a/pkg/integration/deprecated/tui/main.go +++ b/pkg/integration/deprecated/cmd/tui/main.go @@ -108,7 +108,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -122,7 +122,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -136,7 +136,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -150,7 +150,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil @@ -164,7 +164,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name)) + cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name)) app.runSubprocess(cmd) return nil diff --git a/pkg/integration/deprecated/integration.go b/pkg/integration/deprecated/integration.go index 92528b8ad..b44fdb1b2 100644 --- a/pkg/integration/deprecated/integration.go +++ b/pkg/integration/deprecated/integration.go @@ -20,7 +20,7 @@ import ( // Deprecated: This file is part of the old way of doing things. See pkg/integration/integration.go for the new way -// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.mdfor more info. +// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info. type IntegrationTest struct { Name string `json:"name"` diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index f30101e26..68dade273 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -58,7 +58,7 @@ func RunTests( testDir := filepath.Join(rootDir, "test", "integration_new") osCommand := oscommands.NewDummyOSCommand() - err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run() + err = osCommand.Cmd.New("go build pkg/integration/cmd/intector.go -o " + tempLazygitPath()).Run() if err != nil { return err } diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index da25b8f9d..543212d59 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -10,12 +10,12 @@ import ( // to provide to a test in order for the test to run. type IntegrationTest interface { - Run(GuiAdapter) + Run(GuiDriver) SetupConfig(config *config.AppConfig) } // this is the interface through which our integration tests interact with the lazygit gui -type GuiAdapter interface { +type GuiDriver interface { PressKey(string) Keys() config.KeybindingConfig CurrentContext() types.Context From 5e475355bfab17bc8281a230798cba19c7c6ac6c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 13 Aug 2022 13:50:38 +1000 Subject: [PATCH 15/16] add tests for my tests --- pkg/integration/README.md | 2 +- pkg/integration/components/test.go | 13 ++- pkg/integration/components/test_test.go | 105 ++++++++++++++++++ .../{integration_test.go => go_test.go} | 0 .../{integration_test.go => go_test.go} | 0 pkg/integration/integration.go | 2 +- 6 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 pkg/integration/components/test_test.go rename pkg/integration/deprecated/{integration_test.go => go_test.go} (100%) rename pkg/integration/{integration_test.go => go_test.go} (100%) diff --git a/pkg/integration/README.md b/pkg/integration/README.md index 978a20600..01d5786b1 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -39,7 +39,7 @@ There are three ways to invoke a test: 1. go run pkg/integration/cmd/runner/main.go [...] 2. go run pkg/integration/cmd/tui/main.go -3. go test pkg/integration/integration_test.go +3. go test pkg/integration/go_test.go The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests. The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. diff --git a/pkg/integration/components/test.go b/pkg/integration/components/test.go index 2dc60c51f..a5973c07a 100644 --- a/pkg/integration/components/test.go +++ b/pkg/integration/components/test.go @@ -12,6 +12,10 @@ import ( // Test describes an integration tests that will be run against the lazygit gui. +// our unit tests will use this description to avoid a panic caused by attempting +// to get the test's name via it's file's path. +const unitTestDescription = "test test" + type IntegrationTest struct { name string description string @@ -45,8 +49,15 @@ type NewIntegrationTestArgs struct { } func NewIntegrationTest(args NewIntegrationTestArgs) *IntegrationTest { + name := "" + if args.Description != unitTestDescription { + // this panics if we're in a unit test for our integration tests, + // so we're using "test test" as a sentinel value + name = testNameFromFilePath() + } + return &IntegrationTest{ - name: testNameFromFilePath(), + name: name, description: args.Description, extraCmdArgs: args.ExtraCmdArgs, skip: args.Skip, diff --git a/pkg/integration/components/test_test.go b/pkg/integration/components/test_test.go new file mode 100644 index 000000000..3be317424 --- /dev/null +++ b/pkg/integration/components/test_test.go @@ -0,0 +1,105 @@ +package components + +import ( + "testing" + + "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/types" + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" + "github.com/stretchr/testify/assert" +) + +type fakeGuiDriver struct { + failureMessage string + pressedKeys []string +} + +var _ integrationTypes.GuiDriver = &fakeGuiDriver{} + +type GuiDriver interface { + PressKey(string) + Keys() config.KeybindingConfig + CurrentContext() types.Context + Model() *types.Model + Fail(message string) + // These two log methods are for the sake of debugging while testing. There's no need to actually + // commit any logging. + // logs to the normal place that you log to i.e. viewable with `lazygit --logs` + Log(message string) + // logs in the actual UI (in the commands panel) + LogUI(message string) + CheckedOutRef() *models.Branch +} + +func (self *fakeGuiDriver) PressKey(key string) { + self.pressedKeys = append(self.pressedKeys, key) +} + +func (self *fakeGuiDriver) Keys() config.KeybindingConfig { + return config.KeybindingConfig{} +} + +func (self *fakeGuiDriver) CurrentContext() types.Context { + return nil +} + +func (self *fakeGuiDriver) Model() *types.Model { + return &types.Model{Commits: []*models.Commit{}} +} + +func (self *fakeGuiDriver) Fail(message string) { + self.failureMessage = message +} + +func (self *fakeGuiDriver) Log(message string) { +} + +func (self *fakeGuiDriver) LogUI(message string) { +} + +func (self *fakeGuiDriver) CheckedOutRef() *models.Branch { + return nil +} + +func TestAssertionFailure(t *testing.T) { + test := NewIntegrationTest(NewIntegrationTestArgs{ + Description: unitTestDescription, + Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { + input.PressKeys("a") + input.PressKeys("b") + assert.CommitCount(2) + }, + }) + driver := &fakeGuiDriver{} + test.Run(driver) + assert.EqualValues(t, []string{"a", "b"}, driver.pressedKeys) + assert.Equal(t, "Expected 2 commits present, but got 0", driver.failureMessage) +} + +func TestManualFailure(t *testing.T) { + test := NewIntegrationTest(NewIntegrationTestArgs{ + Description: unitTestDescription, + Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { + assert.Fail("blah") + }, + }) + driver := &fakeGuiDriver{} + test.Run(driver) + assert.Equal(t, "blah", driver.failureMessage) +} + +func TestSuccess(t *testing.T) { + test := NewIntegrationTest(NewIntegrationTestArgs{ + Description: unitTestDescription, + Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { + input.PressKeys("a") + input.PressKeys("b") + assert.CommitCount(0) + }, + }) + driver := &fakeGuiDriver{} + test.Run(driver) + assert.EqualValues(t, []string{"a", "b"}, driver.pressedKeys) + assert.Equal(t, "", driver.failureMessage) +} diff --git a/pkg/integration/deprecated/integration_test.go b/pkg/integration/deprecated/go_test.go similarity index 100% rename from pkg/integration/deprecated/integration_test.go rename to pkg/integration/deprecated/go_test.go diff --git a/pkg/integration/integration_test.go b/pkg/integration/go_test.go similarity index 100% rename from pkg/integration/integration_test.go rename to pkg/integration/go_test.go diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index 68dade273..fd7b0fee6 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -58,7 +58,7 @@ func RunTests( testDir := filepath.Join(rootDir, "test", "integration_new") osCommand := oscommands.NewDummyOSCommand() - err = osCommand.Cmd.New("go build pkg/integration/cmd/intector.go -o " + tempLazygitPath()).Run() + err = osCommand.Cmd.New(fmt.Sprintf("go build -o %s pkg/integration/cmd/injector.go", tempLazygitPath())).Run() if err != nil { return err } From cad84c9e7431b33f59d743b1e94353da65b67baa Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 13 Aug 2022 14:12:35 +1000 Subject: [PATCH 16/16] ensure we don't try to run another test when lazygit is invoked as a daemon --- pkg/integration/cmd/injector/main.go | 19 ++++++++++++++----- pkg/integration/integration.go | 6 ++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/pkg/integration/cmd/injector/main.go b/pkg/integration/cmd/injector/main.go index 9460c7b22..2f47d04a6 100644 --- a/pkg/integration/cmd/injector/main.go +++ b/pkg/integration/cmd/injector/main.go @@ -1,9 +1,11 @@ package main import ( + "fmt" "os" "github.com/jesseduffield/lazygit/pkg/app" + "github.com/jesseduffield/lazygit/pkg/app/daemon" "github.com/jesseduffield/lazygit/pkg/integration" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -30,13 +32,20 @@ func main() { } func getIntegrationTest() integrationTypes.IntegrationTest { - integrationTestName := os.Getenv("LAZYGIT_TEST_NAME") - if integrationTestName == "" { - panic("expected LAZYGIT_TEST_NAME environment variable to be set, given that we're running an integration test") + if daemon.InDaemonMode() { + // if we've invoked lazygit as a daemon from within lazygit, + // we don't want to pass a test to the rest of the code. + return nil + } + + integrationTestName := os.Getenv(integration.LAZYGIT_TEST_NAME_ENV_VAR) + if integrationTestName == "" { + panic(fmt.Sprintf( + "expected %s environment variable to be set, given that we're running an integration test", + integration.LAZYGIT_TEST_NAME_ENV_VAR, + )) } - // unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again - os.Unsetenv("LAZYGIT_TEST_NAME") for _, candidateTest := range integration.Tests { if candidateTest.Name() == integrationTestName { return candidateTest diff --git a/pkg/integration/integration.go b/pkg/integration/integration.go index fd7b0fee6..ca71241e2 100644 --- a/pkg/integration/integration.go +++ b/pkg/integration/integration.go @@ -38,6 +38,8 @@ const ( SANDBOX ) +const LAZYGIT_TEST_NAME_ENV_VAR = "LAZYGIT_TEST_NAME" + type ( logf func(format string, formatArgs ...interface{}) ) @@ -58,7 +60,7 @@ func RunTests( testDir := filepath.Join(rootDir, "test", "integration_new") osCommand := oscommands.NewDummyOSCommand() - err = osCommand.Cmd.New(fmt.Sprintf("go build -o %s pkg/integration/cmd/injector.go", tempLazygitPath())).Run() + err = osCommand.Cmd.New(fmt.Sprintf("go build -o %s pkg/integration/cmd/injector/main.go", tempLazygitPath())).Run() if err != nil { return err } @@ -270,7 +272,7 @@ func getLazygitCommand(test *components.IntegrationTest, testPath string, rootDi cmdObj := osCommand.Cmd.New(cmdStr) - cmdObj.AddEnvVars(fmt.Sprintf("LAZYGIT_TEST_NAME=%s", test.Name())) + cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", LAZYGIT_TEST_NAME_ENV_VAR, test.Name())) return cmdObj.GetCmd(), nil }