Repository: clap-rs/clap (language: rust)

Issue:
Set up a single job for completion testing
I found a issue when running the clap_complete tests locally (on the clap master branch) using the command:

```shell=
cargo test --features="unstable-dynamic"
```
The result was:

```shell=
---- expected: tests/testsuite/fish.rs:186:20
++++ actual:   In-memory
   1      - % exhaustive
        1 + % exhaustive
   2    2 | action                                                             last              -V         (Print version)
   3    3 | alias                                                              pacman            --generate      (generate)
   4    4 | complete            (Register shell completions for this program)  quote             --global      (everywhere)
   5    5 | help  (Print this message or the help of the given subcommand(s))  value             --help        (Print help)
   6    6 | hint                                                               -h  (Print help)  --version  (Print version)∅

Update with SNAPSHOTS=overwrite

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    fish::complete_dynamic

test result: FAILED. 72 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.87s

```
There is a difference of a space between them. However, this test case error was not reported in the CI. The issue is may due to this line https://github.com/clap-rs/clap/blob/70e84174a72f766020f5f65f354e16f1e1a49bed/clap_complete/tests/testsuite/fish.rs#L146. So I think that we should make sure the job's environment includes bash, fish, elvish, and zsh shells to cover all test cases in CI.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 725b36bd91f..344527f6ce3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -70,7 +70,9 @@ jobs:
       with:
         toolchain: ${{ matrix.rust }}
     - uses: Swatinem/rust-cache@v2
-    - uses: taiki-e/install-action@cargo-hack
+    - name: Install shells
+      if: runner.os == 'Linux'
+      run: sudo apt-get install -y elvish fish zsh
     - name: Build
       run: make build-${{matrix.features}}
     - name: Test
diff --git a/.github/workflows/rust-next.yml b/.github/workflows/rust-next.yml
index 3b2d4868dc7..a5ac8529f87 100644
--- a/.github/workflows/rust-next.yml
+++ b/.github/workflows/rust-next.yml
@@ -65,6 +65,9 @@ jobs:
       with:
         toolchain: ${{ matrix.rust }}
     - uses: Swatinem/rust-cache@v2
+    - name: Install shells
+      if: runner.os == 'Linux'
+      run: sudo apt-get install -y elvish fish zsh
     - name: Build
       run: make build-${{matrix.features}}
     - name: Test
@@ -96,6 +99,9 @@ jobs:
         toolchain: stable
     - uses: Swatinem/rust-cache@v2
     - uses: taiki-e/install-action@cargo-hack
+    - name: Install shells
+      if: runner.os == 'Linux'
+      run: sudo apt-get install -y elvish fish zsh
     - name: Update dependencues
       run: cargo update
     - name: Build
diff --git a/Cargo.lock b/Cargo.lock
index 00fa5f9a071..0a314e8a9a1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -571,9 +571,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
 [[package]]
 name = "completest"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8229e041ca8f8130ad7f0ce1afb9cfdb3033de7fd548e6422dbb2f4f12184f41"
+checksum = "e6cda99a94266124c2cce3d239973ef8ce3160c83a3f426a314285d9bf6422d1"
 
 [[package]]
 name = "completest-nu"
@@ -594,9 +594,9 @@ dependencies = [
 
 [[package]]
 name = "completest-pty"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a6d1272e27f608f97616be67a2aed03ed8d73910b5df9a7f4a50c4ffd59d185"
+checksum = "ee700748da7d34de4bbe0296d3153e8ef5217233d814d23fb68106c110dd9bc5"
 dependencies = [
  "completest",
  "ptyprocess",
diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml
index bff3f94b880..c9c094239bd 100644
--- a/clap_complete/Cargo.toml
+++ b/clap_complete/Cargo.toml
@@ -45,8 +45,8 @@ unicode-xid = { version = "0.2.2", optional = true }
 snapbox = { version = "0.6.0", features = ["diff", "dir", "examples"] }
 # Cutting out `filesystem` feature
 trycmd = { version = "0.15.1", default-features = false, features = ["color-auto", "diff", "examples"] }
-completest = "0.4.0"
-completest-pty = "0.5.0"
+completest = "0.4.1"
+completest-pty = "0.5.2"
 clap = { path = "../", version = "4.0.0", default-features = false, features = ["std", "derive", "help"] }
 automod = "1.0.14"
 


Test patch:
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.inputrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.inputrc
new file mode 100644
index 00000000000..60f4c090fad
--- /dev/null
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.inputrc
@@ -0,0 +1,1 @@
+# expected empty file to disable loading ~/.inputrc
diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/.zshenv b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/.zshenv
index f7b0671242c..6d309f24b41 100644
--- a/clap_complete/tests/snapshots/home/static/exhaustive/zsh/.zshenv
+++ b/clap_complete/tests/snapshots/home/static/exhaustive/zsh/.zshenv
@@ -1,5 +1,5 @@
 fpath=($fpath $ZDOTDIR/zsh)
-autoload -U +X compinit && compinit
+autoload -U +X compinit && compinit -u # bypass compaudit security checking
 precmd_functions=""  # avoid the prompt being overwritten
 PS1='%% '
 PROMPT='%% '
diff --git a/clap_complete/tests/testsuite/common.rs b/clap_complete/tests/testsuite/common.rs
index f5fe9313d0b..3534344bf12 100644
--- a/clap_complete/tests/testsuite/common.rs
+++ b/clap_complete/tests/testsuite/common.rs
@@ -430,7 +430,7 @@ pub(crate) fn has_command(command: &str) -> bool {
         Ok(output) => output,
         Err(e) => {
             // CI is expected to support all of the commands
-            if is_ci() && cfg!(linux) {
+            if is_ci() && cfg!(target_os = "linux") {
                 panic!(
                     "expected command `{}` to be somewhere in PATH: {}",
                     command, e
diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs
index 7cf9cf7d693..7d5f98b918d 100644
--- a/clap_complete/tests/testsuite/dynamic.rs
+++ b/clap_complete/tests/testsuite/dynamic.rs
@@ -23,14 +23,11 @@ fn suggest_subcommand_subset() {
         .subcommand(Command::new("hello-moon"))
         .subcommand(Command::new("goodbye-world"));
 
-    assert_data_eq!(
-        complete!(cmd, "he"),
-        snapbox::str![
-            "hello-moon
+    assert_data_eq!(complete!(cmd, "he"), snapbox::str![[r#"
+hello-moon
 hello-world
-help\tPrint this message or the help of the given subcommand(s)"
-        ],
-    );
+help	Print this message or the help of the given subcommand(s)
+"#]],);
 }
 
 #[test]
@@ -52,14 +49,11 @@ fn suggest_long_flag_subset() {
                 .action(clap::ArgAction::Count),
         );
 
-    assert_data_eq!(
-        complete!(cmd, "--he"),
-        snapbox::str![
-            "--hello-world
+    assert_data_eq!(complete!(cmd, "--he"), snapbox::str![[r#"
+--hello-world
 --hello-moon
---help\tPrint help"
-        ],
-    );
+--help	Print help
+"#]],);
 }
 
 #[test]
@@ -71,13 +65,10 @@ fn suggest_possible_value_subset() {
         "goodbye-world".into(),
     ]));
 
-    assert_data_eq!(
-        complete!(cmd, "hello"),
-        snapbox::str![
-            "hello-world\tSay hello to the world
-hello-moon"
-        ],
-    );
+    assert_data_eq!(complete!(cmd, "hello"), snapbox::str![[r#"
+hello-world	Say hello to the world
+hello-moon
+"#]],);
 }
 
 #[test]
@@ -99,15 +90,12 @@ fn suggest_additional_short_flags() {
                 .action(clap::ArgAction::Count),
         );
 
-    assert_data_eq!(
-        complete!(cmd, "-a"),
-        snapbox::str![
-            "-aa
+    assert_data_eq!(complete!(cmd, "-a"), snapbox::str![[r#"
+-aa
 -ab
 -ac
--ah\tPrint help"
-        ],
-    );
+-ah	Print help
+"#]],);
 }
 
 #[test]
@@ -120,16 +108,13 @@ fn suggest_subcommand_positional() {
         ]),
     ));
 
-    assert_data_eq!(
-        complete!(cmd, "hello-world [TAB]"),
-        snapbox::str![
-            "--help\tPrint help (see more with '--help')
--h\tPrint help (see more with '--help')
-hello-world\tSay hello to the world
+    assert_data_eq!(complete!(cmd, "hello-world [TAB]"), snapbox::str![[r#"
+--help	Print help (see more with '--help')
+-h	Print help (see more with '--help')
+hello-world	Say hello to the world
 hello-moon
-goodbye-world"
-        ],
-    );
+goodbye-world
+"#]],);
 }
 
 fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {
diff --git a/clap_complete/tests/testsuite/elvish.rs b/clap_complete/tests/testsuite/elvish.rs
index eee5fee6746..17cf1cf6884 100644
--- a/clap_complete/tests/testsuite/elvish.rs
+++ b/clap_complete/tests/testsuite/elvish.rs
@@ -152,8 +152,8 @@ fn complete() {
         common::load_runtime::<completest_pty::ElvishRuntimeBuilder>("static", "exhaustive");
 
     let input = "exhaustive \t";
-    let expected = snapbox::str![
-        r#"% exhaustive --generate
+    let expected = snapbox::str![[r#"
+% exhaustive --generate
  COMPLETING argument  
 --generate     generate                                                 
 --global       everywhere                                               
@@ -169,8 +169,8 @@ hint           hint
 last           last                                                     
 pacman         pacman                                                   
 quote          quote                                                    
-value          value                                                    "#
-    ];
+value          value                                                    
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
diff --git a/clap_complete/tests/testsuite/fish.rs b/clap_complete/tests/testsuite/fish.rs
index 5fc88096f14..c6c11a4e639 100644
--- a/clap_complete/tests/testsuite/fish.rs
+++ b/clap_complete/tests/testsuite/fish.rs
@@ -182,41 +182,42 @@ fn complete_dynamic() {
     let mut runtime =
         common::load_runtime::<completest_pty::FishRuntimeBuilder>("dynamic", "exhaustive");
 
-    let input = "exhaustive \t";
-    let expected = snapbox::str![
-        r#"% exhaustive
+    let input = "exhaustive \t\t";
+    let expected = snapbox::str![[r#"
+% exhaustive action 
 action                                                             last              -V         (Print version)
 alias                                                              pacman            --generate      (generate)
 complete            (Register shell completions for this program)  quote             --global      (everywhere)
 help  (Print this message or the help of the given subcommand(s))  value             --help        (Print help)
-hint                                                               -h  (Print help)  --version  (Print version)"#
-    ];
+hint                                                               -h  (Print help)  --version  (Print version)
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 
-    let input = "exhaustive quote \t";
-    let expected = snapbox::str![
-        r#"% exhaustive quote
-cmd-backslash                                        (Avoid '\n')
+    let input = "exhaustive quote \t\t";
+    let expected = snapbox::str![[r#"
+% exhaustive quote 
+cmd-backslash                                        (Avoid '/n')
 cmd-backticks              (For more information see `echo test`)
 cmd-brackets                             (List packages [filter])
 cmd-double-quotes           (Can be "always", "auto", or "never")
 cmd-expansions            (Execute the shell command with $SHELL)
 cmd-single-quotes           (Can be 'always', 'auto', or 'never')
-escape-help                                             (\tab "')
+escape-help                                             (/tab "')
 help  (Print this message or the help of the given subcommand(s))
--h                                                   (Print help)
+-h                          (Print help (see more with '--help'))
 -V                                                (Print version)
---backslash                                          (Avoid '\n')
+--backslash                                          (Avoid '/n')
 --backticks                (For more information see `echo test`)
 --brackets                               (List packages [filter])
+--choice                                                         
 --double-quotes             (Can be "always", "auto", or "never")
 --expansions              (Execute the shell command with $SHELL)
 --global                                             (everywhere)
---help                                               (Print help)
+--help                      (Print help (see more with '--help'))
 --single-quotes             (Can be 'always', 'auto', or 'never')
---version                                         (Print version)"#
-    ];
+--version                                         (Print version)
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
diff --git a/clap_complete/tests/testsuite/zsh.rs b/clap_complete/tests/testsuite/zsh.rs
index aa6b9132ec8..8acb42a6545 100644
--- a/clap_complete/tests/testsuite/zsh.rs
+++ b/clap_complete/tests/testsuite/zsh.rs
@@ -153,12 +153,12 @@ fn complete() {
         common::load_runtime::<completest_pty::ZshRuntimeBuilder>("static", "exhaustive");
 
     let input = "exhaustive \t";
-    let expected = snapbox::str![
-        r#"% exhaustive
+    let expected = snapbox::str![[r#"
+% exhaustive
 complete                                           -- Register shell completions for this program                     
 help                                               -- Print this message or the help of the given subcommand(s)       
-pacman    action  alias  value  quote  hint  last  --                                                                 "#
-    ];
+pacman    action  alias  value  quote  hint  last  --                                                                 
+"#]];
     let actual = runtime.complete(input, &term).unwrap();
     assert_data_eq!(actual, expected);
 }
diff --git a/clap_complete_nushell/tests/common.rs b/clap_complete_nushell/tests/common.rs
index 871a8baf419..9ed9b5ba82d 100644
--- a/clap_complete_nushell/tests/common.rs
+++ b/clap_complete_nushell/tests/common.rs
@@ -374,7 +374,7 @@ pub(crate) fn has_command(command: &str) -> bool {
         Ok(output) => output,
         Err(e) => {
             // CI is expected to support all of the commands
-            if is_ci() && cfg!(linux) {
+            if is_ci() && cfg!(target_os = "linux") {
                 panic!(
                     "expected command `{}` to be somewhere in PATH: {}",
                     command, e