@@ -379,21 +379,6 @@ function bashunit::coverage::get_all_line_hits() {
379379 done
380380}
381381
382- # Get the tests that hit a specific line
383- # Output format: list of "test_file:test_function" (unique, sorted)
384- function bashunit::coverage::get_line_tests() {
385- local file=" $1 "
386- local lineno=" $2 "
387-
388- if [[ ! -f " ${_BASHUNIT_COVERAGE_TEST_HITS_FILE:- } " ]]; then
389- return
390- fi
391-
392- # Format in file: source_file:line|test_file:test_function
393- grep " ^${file} :${lineno} |" " $_BASHUNIT_COVERAGE_TEST_HITS_FILE " 2> /dev/null | \
394- cut -d' |' -f2 | sort -u
395- }
396-
397382# Get all test hits for a file in one pass (performance optimization)
398383# Output format: lineno|test_file:test_function (may have duplicates, one per hit)
399384function bashunit::coverage::get_all_line_tests() {
@@ -715,9 +700,6 @@ function bashunit::coverage::generate_index_html() {
715700 <meta charset="UTF-8">
716701 <meta name="viewport" content="width=device-width, initial-scale=1.0">
717702 <title>Coverage Report | bashunit</title>
718- <link rel="preconnect" href="https://fonts.googleapis.com">
719- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
720- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
721703 <style>
722704 :root {
723705 --primary: #6366f1; --primary-dark: #4f46e5; --primary-light: #818cf8;
@@ -729,7 +711,7 @@ function bashunit::coverage::generate_index_html() {
729711 --border: #e2e8f0;
730712 }
731713 * { margin: 0; padding: 0; box-sizing: border-box; }
732- body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-light); color: var(--text-primary); min-height: 100vh; line-height: 1.6; }
714+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial , sans-serif; background: var(--bg-light); color: var(--text-primary); min-height: 100vh; line-height: 1.6; }
733715 .header { background: var(--bg-card); padding: 0; position: relative; overflow: hidden; border-bottom: 1px solid var(--border); }
734716 .header-content { position: relative; z-index: 1; max-width: 1400px; margin: 0 auto; padding: 40px 30px; }
735717 .header-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; }
@@ -789,7 +771,7 @@ function bashunit::coverage::generate_index_html() {
789771 .file-info { display: flex; flex-direction: column; gap: 4px; }
790772 .file-name { font-weight: 600; color: var(--text-primary); text-decoration: none; font-size: 1rem; transition: color 0.2s; }
791773 .file-name:hover { color: var(--primary-light); }
792- .file-path { color: var(--text-muted); font-size: 0.85rem; font-family: 'JetBrains Mono', monospace; }
774+ .file-path { color: var(--text-muted); font-size: 0.85rem; font-family: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo , monospace; }
793775 .lines-info { text-align: center; }
794776 .lines-covered { font-weight: 700; font-size: 1.1rem; color: var(--text-primary); }
795777 .lines-total { color: var(--text-muted); font-size: 0.85rem; }
@@ -1056,17 +1038,19 @@ function bashunit::coverage::generate_file_html() {
10561038 hits_by_line[_ln]=$_cnt
10571039 done < <( bashunit::coverage::get_all_line_hits " $file " )
10581040
1059- # Pre-load test hits data into associative array (for tooltips)
1060- # Key: line number, Value: newline-separated list of "test_file:test_function"
1061- declare -A tests_by_line
1041+ # Pre-load test hits data into indexed array (for tooltips)
1042+ # Index: line number, Value: newline-separated list of "test_file:test_function"
1043+ # Using indexed array for Bash 3.2 compatibility (no associative arrays)
1044+ local -a tests_by_line=()
10621045 local _line_and_test
10631046 while IFS= read -r _line_and_test; do
10641047 [[ -z " $_line_and_test " ]] && continue
10651048 local _tln=" ${_line_and_test%% |* } "
10661049 local _tinfo=" ${_line_and_test#* |} "
10671050 if [[ -n " ${tests_by_line[$_tln]:- } " ]]; then
10681051 # Append only if not already present (avoid duplicates)
1069- if [[ " ${tests_by_line[$_tln]} " != * " $_tinfo " * ]]; then
1052+ # Use newline boundaries to prevent false positives (e.g., test_foo matching test_foo_bar)
1053+ if [[ $' \n ' " ${tests_by_line[$_tln]} " $' \n ' != * $' \n ' " $_tinfo " $' \n ' * ]]; then
10701054 tests_by_line[$_tln ]=" ${tests_by_line[$_tln]} " $' \n ' " ${_tinfo} "
10711055 fi
10721056 else
@@ -1089,9 +1073,6 @@ function bashunit::coverage::generate_file_html() {
10891073EOF
10901074 echo " <title>$( basename " $display_file " ) | Coverage Report</title>"
10911075 cat << 'EOF '
1092- <link rel="preconnect" href="https://fonts.googleapis.com">
1093- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1094- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
10951076 <style>
10961077 :root {
10971078 --primary: #6366f1; --primary-dark: #4f46e5; --primary-light: #818cf8;
@@ -1103,14 +1084,14 @@ EOF
11031084 --border: #e2e8f0; --line-number-bg: #f8fafc;
11041085 }
11051086 * { margin: 0; padding: 0; box-sizing: border-box; }
1106- body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-light); color: var(--text-primary); min-height: 100vh; line-height: 1.6; }
1087+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial , sans-serif; background: var(--bg-light); color: var(--text-primary); min-height: 100vh; line-height: 1.6; }
11071088 .header { background: var(--bg-card); border-bottom: 1px solid var(--border); padding: 20px 30px; position: sticky; top: 0; z-index: 100; backdrop-filter: blur(10px); box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
11081089 .header-content { max-width: 1600px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px; }
11091090 .nav-section { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
11101091 .back-btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; background: #475569; border: 2px solid #475569; border-radius: 8px; color: #ffffff; text-decoration: none; font-size: 1rem; font-weight: 600; transition: all 0.2s; box-shadow: 0 2px 4px rgba(71, 85, 105, 0.2); }
11111092 .back-btn:hover { background: #334155; border-color: #334155; box-shadow: 0 4px 12px rgba(51, 65, 85, 0.3); }
11121093 .file-title { display: flex; align-items: center; gap: 12px; }
1113- .file-name { font-size: 1.3rem; font-weight: 700; font-family: 'JetBrains Mono', monospace; }
1094+ .file-name { font-size: 1.3rem; font-weight: 700; font-family: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo , monospace; }
11141095 .stats-section { display: flex; align-items: center; gap: 30px; flex-wrap: wrap; }
11151096 .stat-item { display: flex; align-items: center; gap: 10px; }
11161097 .stat-badge { padding: 8px 16px; border-radius: 20px; font-weight: 600; font-size: 0.9rem; }
@@ -1142,11 +1123,11 @@ EOF
11421123 .code-container { max-width: 1600px; margin: 30px auto; padding: 0 30px; }
11431124 .code-wrapper { background: var(--bg-code); border-radius: 16px; overflow: hidden; border: 1px solid var(--border); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); }
11441125 .code-header { background: var(--line-number-bg); padding: 16px 24px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); flex-wrap: wrap; gap: 12px; }
1145- .code-path { font-family: 'JetBrains Mono', monospace; font-size: 0.9rem; color: var(--text-secondary); }
1126+ .code-path { font-family: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo , monospace; font-size: 0.9rem; color: var(--text-secondary); }
11461127 .code-stats { display: flex; gap: 16px; font-size: 0.85rem; }
11471128 .code-stats span { padding: 4px 12px; background: #e5e7eb; border-radius: 4px; color: var(--text-secondary); }
11481129 .code-body { overflow-x: auto; }
1149- .code-table { width: 100%; border-collapse: collapse; font-family: 'JetBrains Mono', monospace; font-size: 13px; line-height: 1.6; }
1130+ .code-table { width: 100%; border-collapse: collapse; font-family: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo , monospace; font-size: 13px; line-height: 1.6; }
11501131 .code-table tr { transition: background 0.15s; }
11511132 .line-num { width: 60px; padding: 2px 16px; text-align: right; color: #9ca3af; background: var(--line-number-bg); border-right: 1px solid var(--border); user-select: none; vertical-align: top; }
11521133 .hits { width: 60px; padding: 2px 12px; text-align: center; color: #9ca3af; background: var(--line-number-bg); border-right: 1px solid var(--border); font-size: 0.85em; vertical-align: top; }
@@ -1159,7 +1140,7 @@ EOF
11591140 .hits-badge:hover .hits-tooltip { display: block; }
11601141 .hits-tooltip-title { font-weight: 600; margin-bottom: 6px; color: #94a3b8; font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
11611142 .hits-tooltip-list { margin: 0; padding: 0; list-style: none; }
1162- .hits-tooltip-list li { padding: 3px 0; border-bottom: 1px solid #334155; font-family: 'JetBrains Mono', monospace; }
1143+ .hits-tooltip-list li { padding: 3px 0; border-bottom: 1px solid #334155; font-family: 'SF Mono', 'Consolas', 'Liberation Mono', Menlo , monospace; }
11631144 .hits-tooltip-list li:last-child { border-bottom: none; }
11641145 .hits-tooltip-file { color: #60a5fa; }
11651146 .hits-tooltip-fn { color: #a5b4fc; }
0 commit comments