Repository: reubeno/brush
Pull request #386 (2025-02-19 09:55:15)

fix(extglob): correct handling of extglobs with empty branches
Corrects parsing of `extglob` patterns such as `@(|text)`.

github-actions[bot]: ## Test Results
    2 files      9 suites   1m 42s ⏱️
  687 tests   687 ✅ 0 💤 0 ❌
1 360 runs  1 360 ✅ 0 💤 0 ❌

Results for commit ca6e571b.

[test-results]:data:application/gzip;base64,H4sIAEattWcC/12MSw7CIBQAr9KwdsEnUPAyBukjebEthsKq8e4CEW1dzkwyO/E4w0auA78MZMuYGpgCU442YVgLMlprSalGpcdOty07968e+CyKfoW3OJ8ExBjix8S81icTqlN/ntTv2fiwbHw8urAsmAoQZxXIkd2N1pyDUF5YDVZIT4VyFLicDOWgLXm9AfrJHm4HAQAA

github-actions[bot]: # Performance Benchmark Report
| Benchmark name                         | Baseline (μs) |  Test/PR (μs) |    Delta (μs) | Delta %         |
| -------------------------------------- | ------------- | ------------- | ------------- | --------------- |
| `clone_shell_object                  ` | `   17.94 μs` | `   17.91 μs` | `   -0.03 μs` | `⚪  Unchanged` |
| `eval_arithmetic                     ` | `    0.17 μs` | `    0.17 μs` | `    0.00 μs` | `⚪  Unchanged` |
| `expand_one_string                   ` | `    1.81 μs` | `    1.79 μs` | `   -0.01 μs` | `⚪  Unchanged` |
| `for_loop                            ` | `   21.97 μs` | `   21.71 μs` | `   -0.26 μs` | `🟢 -1.19%    ` |
| `function_call                       ` | `    2.36 μs` | `    2.27 μs` | `   -0.09 μs` | `⚪  Unchanged` |
| `instantiate_shell                   ` | `   57.45 μs` | `   56.82 μs` | `   -0.62 μs` | `🟢 -1.08%    ` |
| `instantiate_shell_with_init_scripts ` | `21285.34 μs` | `21447.17 μs` | `  161.83 μs` | `⚪  Unchanged` |
| `parse_bash_completion               ` | ` 1685.36 μs` | ` 1673.81 μs` | `  -11.55 μs` | `⚪  Unchanged` |
| `parse_sample_script                 ` | `    1.77 μs` | `    1.75 μs` | `   -0.01 μs` | `🟢 -0.85%    ` |
| `run_echo_builtin_command            ` | `   15.24 μs` | `   15.27 μs` | `    0.03 μs` | `⚪  Unchanged` |
| `run_one_external_command            ` | ` 2293.26 μs` | ` 2330.78 μs` | `   37.52 μs` | `🟠 +1.64%    ` |
| `tokenize_sample_script              ` | `    2.80 μs` | `    2.89 μs` | `    0.09 μs` | `🟠 +3.18%    ` |

# Code Coverage Report: Only Changed Files listed

| Package | Base Coverage | New Coverage | Difference |
| ------- | ------------- | ------------ | ---------- |
| brush-core/src/jobs.rs | 🔴 37.07% | 🔴 43.1% | 🟢 6.03% |
| **Overall Coverage** | **🟢 78.24%** | **🟢 78.34%** | **🟢 0.1%** |

_Minimum allowed coverage is_ `70%`_, this run produced_ `78.34%`

# Test Summary: bash-completion test suite
| Outcome            | Count                   | Percentage |
| ------------------ | ----------------------: | ---------: |
| ✅ Pass            | 1317            | <span style='color:green'>62.71</span> |
| ❗️ Error           | 229           | <span style='color:red'>10.90</span> |
| ❌ Fail            | 175            | <span style='color:red'>8.33</span> |
| ⏩ Skip            | 364            | 17.33 |
| ❎ Expected Fail   | 14   | 0.67 |
| ✔️ Unexpected Pass | 1 | <span style='color:red'>0.05</span> |
| 📊 Total           | 2100           | 100.00 |

<!-- Sticky Pull Request Comment -->

Patch:
diff --git a/brush-parser/src/pattern.rs b/brush-parser/src/pattern.rs
index 7b3212d..e833c6c 100644
--- a/brush-parser/src/pattern.rs
+++ b/brush-parser/src/pattern.rs
@@ -130,9 +130,14 @@ peg::parser! {
             "*" { ExtendedGlobKind::Star }
 
         pub(crate) rule extended_glob_body() -> Vec<String> =
+            // Cover case with *no* branches.
+            &[')'] { vec![] } /
+            // Otherwise, look for branches separated by '|'.
             extended_glob_branch() ** "|"
 
         rule extended_glob_branch() -> String =
+            // Cover case of empty branch.
+            &['|' | ')'] { String::new() } /
             pieces:(!['|' | ')'] piece:pattern_piece() { piece })+ {
                 pieces.join("")
             }
@@ -179,6 +184,16 @@ mod tests {
             "(a|b)"
         );
 
+        assert_eq!(
+            pattern_to_regex_translator::extended_glob_pattern("@(|a)", true)?,
+            "(|a)"
+        );
+
+        assert_eq!(
+            pattern_to_regex_translator::extended_glob_pattern("@(|)", true)?,
+            "(|)"
+        );
+
         assert_eq!(
             pattern_to_regex_translator::extended_glob_body("ab|ac", true)?,
             vec!["ab", "ac"],


Test patch:
diff --git a/brush-shell/tests/cases/builtins/declare.yaml b/brush-shell/tests/cases/builtins/declare.yaml
index aad8ec2..9dfe11f 100644
--- a/brush-shell/tests/cases/builtins/declare.yaml
+++ b/brush-shell/tests/cases/builtins/declare.yaml
@@ -24,20 +24,22 @@ cases:
 
   - name: "Display vars with interesting chars"
     stdin: |
-      (testvar=$'a\nb' && declare -p testvar && declare | grep testvar=)
-      echo "-------------------------"
       (testvar="\"abc\"" && declare -p testvar && declare | grep testvar=)
       echo "-------------------------"
       (testvar="a b c" && declare -p testvar && declare | grep testvar=)
       echo "-------------------------"
       (testvar="'" && declare -p testvar && declare | grep testvar=)
+
+  - name: "Display vars with interesting chars 2"
+    min_oracle_version: 5.2 # some sequences render differently in older shell versions
+    stdin: |
+      (testvar=$'a\nb' && declare -p testvar && declare | grep testvar=)
       echo "-------------------------"
       (testvar=$'\x03' && declare -p testvar && declare | grep testvar=)
       echo "-------------------------"
       (testvar=$'\x08' && declare -p testvar && declare | grep testvar=)
       echo "-------------------------"
       (testvar=$(printf '\033[34mabc\033[0m') && declare -p testvar && declare | grep testvar=)
-      echo "-------------------------"
 
   - name: "Declare integer"
     stdin: |
diff --git a/brush-shell/tests/cases/builtins/read.yaml b/brush-shell/tests/cases/builtins/read.yaml
index 8ba5cbc..ee1a682 100644
--- a/brush-shell/tests/cases/builtins/read.yaml
+++ b/brush-shell/tests/cases/builtins/read.yaml
@@ -78,5 +78,6 @@ cases:
       (echo 'x,,y,z'; echo 'w,v'; echo ''; echo ''; echo 'm') | (IFS=',' read -d "" -a READ; declare -p READ)
 
   - name: "read with empty delimiter"
+    min_oracle_version: 5.2 # \n renders differently in older shell versions
     stdin: |
       echo x | (read -d ""; declare -p REPLY)
diff --git a/brush-shell/tests/cases/patterns/filename_expansion.yaml b/brush-shell/tests/cases/patterns/filename_expansion.yaml
index ec4d2d6..c970399 100644
--- a/brush-shell/tests/cases/patterns/filename_expansion.yaml
+++ b/brush-shell/tests/cases/patterns/filename_expansion.yaml
@@ -159,6 +159,7 @@ cases:
       echo "1: " @(abc|abd).txt
       echo "2: " @(abc.txt)
       echo "3: " @(abc)
+      echo "4: " @(|abc.txt)
 
   - name: "Pathname expansion: Optional patterns"
     ignore_stderr: true