cov-loupe 3.0.0 → 4.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +230 -0
- data/CLAUDE.md +5 -0
- data/CODE_OF_CONDUCT.md +62 -0
- data/CONTRIBUTING.md +102 -0
- data/GEMINI.md +5 -0
- data/README.md +154 -51
- data/RELEASE_NOTES.md +452 -0
- data/dev/images/cov-loupe-icon-lores.png +0 -0
- data/dev/images/cov-loupe-icon-square.png +0 -0
- data/dev/images/cov-loupe-icon.png +0 -0
- data/dev/images/cov-loupe-logo.png +0 -0
- data/dev/prompts/README.md +74 -0
- data/dev/prompts/archive/architectural-review-and-actions-prompt.md +53 -0
- data/dev/prompts/archive/investigate-and-report-issues-prompt.md +33 -0
- data/dev/prompts/archive/produce-action-items-prompt.md +25 -0
- data/dev/prompts/guidelines/ai-code-evaluator-guidelines.md +337 -0
- data/dev/prompts/improve/refactor-test-suite.md +18 -0
- data/dev/prompts/improve/simplify-code-logic.md +133 -0
- data/dev/prompts/improve/update-documentation.md +21 -0
- data/dev/prompts/review/comprehensive-codebase-review.md +176 -0
- data/dev/prompts/review/identify-action-items.md +143 -0
- data/dev/prompts/review/verify-code-changes.md +54 -0
- data/dev/prompts/validate/create-screencast-outline.md +234 -0
- data/dev/prompts/validate/test-documentation-examples.md +180 -0
- data/docs/QUICKSTART.md +63 -0
- data/docs/assets/images/cov-loupe-logo-lores.png +0 -0
- data/docs/assets/images/cov-loupe-logo.png +0 -0
- data/docs/assets/images/favicon.png +0 -0
- data/docs/assets/stylesheets/branding.css +16 -0
- data/docs/assets/stylesheets/extra.css +15 -0
- data/docs/code_of_conduct.md +1 -0
- data/docs/contributing.md +1 -0
- data/docs/dev/ARCHITECTURE.md +56 -11
- data/docs/dev/DEVELOPMENT.md +116 -12
- data/docs/dev/FUTURE_ENHANCEMENTS.md +14 -0
- data/docs/dev/README.md +3 -2
- data/docs/dev/RELEASING.md +2 -0
- data/docs/dev/arch-decisions/README.md +10 -7
- data/docs/dev/arch-decisions/application-architecture.md +259 -0
- data/docs/dev/arch-decisions/coverage-data-quality.md +193 -0
- data/docs/dev/arch-decisions/output-character-mode.md +217 -0
- data/docs/dev/arch-decisions/path-resolution.md +90 -0
- data/docs/dev/arch-decisions/{004-x-arch-decision.md → policy-validation.md} +32 -28
- data/docs/dev/arch-decisions/{005-x-arch-decision.md → simplecov-integration.md} +47 -44
- data/docs/dev/presentations/cov-loupe-presentation.md +15 -13
- data/docs/examples/mcp-inputs.md +3 -0
- data/docs/examples/prompts.md +3 -0
- data/docs/examples/success_predicates.md +3 -0
- data/docs/fixtures/demo_project/.resultset.json +170 -0
- data/docs/fixtures/demo_project/README.md +6 -0
- data/docs/fixtures/demo_project/app/controllers/admin/audit_logs_controller.rb +19 -0
- data/docs/fixtures/demo_project/app/controllers/orders_controller.rb +26 -0
- data/docs/fixtures/demo_project/app/models/order.rb +20 -0
- data/docs/fixtures/demo_project/app/models/user.rb +19 -0
- data/docs/fixtures/demo_project/lib/api/client.rb +22 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/cleanup_job.rb +16 -0
- data/docs/fixtures/demo_project/lib/ops/jobs/report_job.rb +17 -0
- data/docs/fixtures/demo_project/lib/payments/processor.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/refund_service.rb +15 -0
- data/docs/fixtures/demo_project/lib/payments/reporting/exporter.rb +16 -0
- data/docs/index.md +1 -0
- data/docs/license.md +3 -0
- data/docs/release_notes.md +3 -0
- data/docs/user/ADVANCED_USAGE.md +208 -115
- data/docs/user/CLI_FALLBACK_FOR_LLMS.md +2 -0
- data/docs/user/CLI_USAGE.md +276 -101
- data/docs/user/ERROR_HANDLING.md +4 -4
- data/docs/user/EXAMPLES.md +121 -128
- data/docs/user/INSTALLATION.md +9 -28
- data/docs/user/LIBRARY_API.md +227 -122
- data/docs/user/MCP_INTEGRATION.md +114 -203
- data/docs/user/README.md +5 -1
- data/docs/user/TROUBLESHOOTING.md +49 -27
- data/docs/user/installing-a-prelease-version-of-covloupe.md +43 -0
- data/docs/user/{V2-BREAKING-CHANGES.md → migrations/MIGRATING_TO_V2.md} +62 -72
- data/docs/user/migrations/MIGRATING_TO_V3.md +72 -0
- data/docs/user/migrations/MIGRATING_TO_V4.md +591 -0
- data/docs/user/migrations/README.md +22 -0
- data/docs/user/prompts/README.md +9 -0
- data/docs/user/prompts/non-web-coverage-analysis-prompt.md +103 -0
- data/docs/user/prompts/rails-coverage-analysis-prompt.md +94 -0
- data/docs/user/prompts/use-cli-not-mcp-prompt.md +53 -0
- data/examples/cli_demo.sh +77 -0
- data/examples/filter_and_table_demo-output.md +114 -0
- data/examples/filter_and_table_demo.rb +174 -0
- data/examples/fixtures/demo_project/coverage/.resultset.json +10 -0
- data/examples/mcp-inputs/README.md +66 -0
- data/examples/mcp-inputs/coverage_detailed.json +1 -0
- data/examples/mcp-inputs/coverage_raw.json +1 -0
- data/examples/mcp-inputs/coverage_summary.json +1 -0
- data/examples/mcp-inputs/list.json +1 -0
- data/examples/mcp-inputs/uncovered_lines.json +1 -0
- data/examples/prompts/README.md +27 -0
- data/examples/prompts/custom_resultset.txt +2 -0
- data/examples/prompts/detailed_with_source.txt +2 -0
- data/examples/prompts/list_lowest.txt +2 -0
- data/examples/prompts/summary.txt +2 -0
- data/examples/prompts/uncovered.txt +2 -0
- data/examples/success_predicates/README.md +198 -0
- data/examples/success_predicates/all_files_above_threshold_predicate.rb +21 -0
- data/examples/success_predicates/directory_specific_thresholds_predicate.rb +30 -0
- data/examples/success_predicates/project_coverage_minimum_predicate.rb +6 -0
- data/lib/cov_loupe/base_tool.rb +229 -20
- data/lib/cov_loupe/cli.rb +132 -23
- data/lib/cov_loupe/commands/base_command.rb +25 -6
- data/lib/cov_loupe/commands/command_factory.rb +0 -1
- data/lib/cov_loupe/commands/detailed_command.rb +10 -5
- data/lib/cov_loupe/commands/list_command.rb +2 -1
- data/lib/cov_loupe/commands/raw_command.rb +7 -5
- data/lib/cov_loupe/commands/summary_command.rb +12 -7
- data/lib/cov_loupe/commands/totals_command.rb +74 -10
- data/lib/cov_loupe/commands/uncovered_command.rb +7 -5
- data/lib/cov_loupe/commands/validate_command.rb +11 -3
- data/lib/cov_loupe/commands/version_command.rb +6 -4
- data/lib/cov_loupe/{app_config.rb → config/app_config.rb} +13 -5
- data/lib/cov_loupe/config/app_context.rb +43 -0
- data/lib/cov_loupe/config/boolean_type.rb +91 -0
- data/lib/cov_loupe/config/logger.rb +92 -0
- data/lib/cov_loupe/{option_normalizers.rb → config/option_normalizers.rb} +55 -24
- data/lib/cov_loupe/{option_parser_builder.rb → config/option_parser_builder.rb} +46 -24
- data/lib/cov_loupe/coverage/coverage_calculator.rb +53 -0
- data/lib/cov_loupe/coverage/coverage_reporter.rb +63 -0
- data/lib/cov_loupe/coverage/coverage_table_formatter.rb +133 -0
- data/lib/cov_loupe/{error_handler.rb → errors/error_handler.rb} +21 -33
- data/lib/cov_loupe/{errors.rb → errors/errors.rb} +48 -71
- data/lib/cov_loupe/formatters/formatters.rb +75 -0
- data/lib/cov_loupe/formatters/source_formatter.rb +18 -7
- data/lib/cov_loupe/formatters/table_formatter.rb +80 -0
- data/lib/cov_loupe/loaders/all.rb +15 -0
- data/lib/cov_loupe/loaders/all_cli.rb +10 -0
- data/lib/cov_loupe/loaders/all_mcp.rb +23 -0
- data/lib/cov_loupe/loaders/resultset_loader.rb +147 -0
- data/lib/cov_loupe/mcp_server.rb +3 -2
- data/lib/cov_loupe/model/model.rb +520 -0
- data/lib/cov_loupe/model/model_data.rb +13 -0
- data/lib/cov_loupe/model/model_data_cache.rb +116 -0
- data/lib/cov_loupe/option_parsers/env_options_parser.rb +17 -6
- data/lib/cov_loupe/option_parsers/error_helper.rb +16 -10
- data/lib/cov_loupe/output_chars.rb +192 -0
- data/lib/cov_loupe/paths/glob_utils.rb +100 -0
- data/lib/cov_loupe/{path_relativizer.rb → paths/path_relativizer.rb} +5 -13
- data/lib/cov_loupe/paths/path_utils.rb +265 -0
- data/lib/cov_loupe/paths/volume_case_sensitivity.rb +173 -0
- data/lib/cov_loupe/presenters/base_coverage_presenter.rb +9 -13
- data/lib/cov_loupe/presenters/coverage_payload_presenter.rb +21 -0
- data/lib/cov_loupe/presenters/payload_caching.rb +23 -0
- data/lib/cov_loupe/presenters/project_coverage_presenter.rb +73 -21
- data/lib/cov_loupe/presenters/project_totals_presenter.rb +16 -10
- data/lib/cov_loupe/repositories/coverage_repository.rb +149 -0
- data/lib/cov_loupe/resolvers/coverage_line_resolver.rb +90 -76
- data/lib/cov_loupe/resolvers/{resolver_factory.rb → resolver_helpers.rb} +6 -5
- data/lib/cov_loupe/resolvers/resultset_path_resolver.rb +40 -12
- data/lib/cov_loupe/scripts/command_execution.rb +113 -0
- data/lib/cov_loupe/scripts/latest_ci_status.rb +97 -0
- data/lib/cov_loupe/scripts/pre_release_check.rb +164 -0
- data/lib/cov_loupe/scripts/setup_doc_server.rb +23 -0
- data/lib/cov_loupe/scripts/start_doc_server.rb +24 -0
- data/lib/cov_loupe/staleness/stale_status.rb +23 -0
- data/lib/cov_loupe/staleness/staleness_checker.rb +328 -0
- data/lib/cov_loupe/staleness/staleness_message_formatter.rb +91 -0
- data/lib/cov_loupe/tools/coverage_detailed_tool.rb +14 -15
- data/lib/cov_loupe/tools/coverage_raw_tool.rb +14 -14
- data/lib/cov_loupe/tools/coverage_summary_tool.rb +16 -16
- data/lib/cov_loupe/tools/coverage_table_tool.rb +139 -21
- data/lib/cov_loupe/tools/coverage_totals_tool.rb +31 -13
- data/lib/cov_loupe/tools/help_tool.rb +16 -20
- data/lib/cov_loupe/tools/list_tool.rb +65 -0
- data/lib/cov_loupe/tools/uncovered_lines_tool.rb +14 -14
- data/lib/cov_loupe/tools/validate_tool.rb +18 -24
- data/lib/cov_loupe/tools/version_tool.rb +8 -3
- data/lib/cov_loupe/version.rb +1 -1
- data/lib/cov_loupe.rb +83 -55
- metadata +184 -154
- data/docs/dev/BRANCH_ONLY_COVERAGE.md +0 -158
- data/docs/dev/arch-decisions/001-x-arch-decision.md +0 -95
- data/docs/dev/arch-decisions/002-x-arch-decision.md +0 -159
- data/docs/dev/arch-decisions/003-x-arch-decision.md +0 -165
- data/lib/cov_loupe/app_context.rb +0 -26
- data/lib/cov_loupe/constants.rb +0 -22
- data/lib/cov_loupe/coverage_reporter.rb +0 -31
- data/lib/cov_loupe/formatters.rb +0 -51
- data/lib/cov_loupe/mode_detector.rb +0 -56
- data/lib/cov_loupe/model.rb +0 -339
- data/lib/cov_loupe/presenters/coverage_detailed_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_raw_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_summary_presenter.rb +0 -14
- data/lib/cov_loupe/presenters/coverage_uncovered_presenter.rb +0 -14
- data/lib/cov_loupe/resultset_loader.rb +0 -131
- data/lib/cov_loupe/staleness_checker.rb +0 -247
- data/lib/cov_loupe/table_formatter.rb +0 -64
- data/lib/cov_loupe/tools/all_files_coverage_tool.rb +0 -51
- data/lib/cov_loupe/util.rb +0 -88
- data/spec/MCP_INTEGRATION_TESTS_README.md +0 -111
- data/spec/TIMESTAMPS.md +0 -48
- data/spec/all_files_coverage_tool_spec.rb +0 -53
- data/spec/app_config_spec.rb +0 -142
- data/spec/base_tool_spec.rb +0 -62
- data/spec/cli/show_default_report_spec.rb +0 -33
- data/spec/cli_enumerated_options_spec.rb +0 -90
- data/spec/cli_error_spec.rb +0 -184
- data/spec/cli_format_spec.rb +0 -123
- data/spec/cli_json_options_spec.rb +0 -50
- data/spec/cli_source_spec.rb +0 -44
- data/spec/cli_spec.rb +0 -192
- data/spec/cli_table_spec.rb +0 -28
- data/spec/cli_usage_spec.rb +0 -42
- data/spec/commands/base_command_spec.rb +0 -107
- data/spec/commands/command_factory_spec.rb +0 -76
- data/spec/commands/detailed_command_spec.rb +0 -34
- data/spec/commands/list_command_spec.rb +0 -28
- data/spec/commands/raw_command_spec.rb +0 -69
- data/spec/commands/summary_command_spec.rb +0 -34
- data/spec/commands/totals_command_spec.rb +0 -34
- data/spec/commands/uncovered_command_spec.rb +0 -55
- data/spec/commands/validate_command_spec.rb +0 -213
- data/spec/commands/version_command_spec.rb +0 -38
- data/spec/constants_spec.rb +0 -61
- data/spec/cov_loupe/formatters/source_formatter_spec.rb +0 -267
- data/spec/cov_loupe/formatters_spec.rb +0 -76
- data/spec/cov_loupe/presenters/base_coverage_presenter_spec.rb +0 -79
- data/spec/cov_loupe_model_spec.rb +0 -454
- data/spec/cov_loupe_module_spec.rb +0 -37
- data/spec/cov_loupe_opts_spec.rb +0 -185
- data/spec/coverage_reporter_spec.rb +0 -102
- data/spec/coverage_table_tool_spec.rb +0 -59
- data/spec/coverage_totals_tool_spec.rb +0 -37
- data/spec/error_handler_spec.rb +0 -197
- data/spec/error_mode_spec.rb +0 -139
- data/spec/errors_edge_cases_spec.rb +0 -312
- data/spec/errors_stale_spec.rb +0 -83
- data/spec/file_based_mcp_tools_spec.rb +0 -99
- data/spec/help_tool_spec.rb +0 -26
- data/spec/integration_spec.rb +0 -789
- data/spec/logging_fallback_spec.rb +0 -128
- data/spec/mcp_logging_spec.rb +0 -44
- data/spec/mcp_server_integration_spec.rb +0 -23
- data/spec/mcp_server_spec.rb +0 -106
- data/spec/mode_detector_spec.rb +0 -153
- data/spec/model_error_handling_spec.rb +0 -269
- data/spec/model_staleness_spec.rb +0 -79
- data/spec/option_normalizers_spec.rb +0 -203
- data/spec/option_parsers/env_options_parser_spec.rb +0 -221
- data/spec/option_parsers/error_helper_spec.rb +0 -222
- data/spec/path_relativizer_spec.rb +0 -98
- data/spec/presenters/coverage_detailed_presenter_spec.rb +0 -19
- data/spec/presenters/coverage_raw_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_summary_presenter_spec.rb +0 -15
- data/spec/presenters/coverage_uncovered_presenter_spec.rb +0 -16
- data/spec/presenters/project_coverage_presenter_spec.rb +0 -87
- data/spec/presenters/project_totals_presenter_spec.rb +0 -144
- data/spec/resolvers/coverage_line_resolver_spec.rb +0 -282
- data/spec/resolvers/resolver_factory_spec.rb +0 -61
- data/spec/resolvers/resultset_path_resolver_spec.rb +0 -60
- data/spec/resultset_loader_spec.rb +0 -167
- data/spec/shared_examples/README.md +0 -115
- data/spec/shared_examples/coverage_presenter_examples.rb +0 -66
- data/spec/shared_examples/file_based_mcp_tools.rb +0 -179
- data/spec/shared_examples/formatted_command_examples.rb +0 -64
- data/spec/shared_examples/mcp_tool_text_json_response.rb +0 -16
- data/spec/spec_helper.rb +0 -127
- data/spec/staleness_checker_spec.rb +0 -374
- data/spec/staleness_more_spec.rb +0 -42
- data/spec/support/cli_helpers.rb +0 -22
- data/spec/support/control_flow_helpers.rb +0 -20
- data/spec/support/fake_mcp.rb +0 -40
- data/spec/support/io_helpers.rb +0 -29
- data/spec/support/mcp_helpers.rb +0 -35
- data/spec/support/mcp_runner.rb +0 -66
- data/spec/support/mocking_helpers.rb +0 -30
- data/spec/table_format_spec.rb +0 -70
- data/spec/tools/validate_tool_spec.rb +0 -132
- data/spec/tools_error_handling_spec.rb +0 -130
- data/spec/util_spec.rb +0 -154
- data/spec/version_spec.rb +0 -123
- data/spec/version_tool_spec.rb +0 -141
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/bar.rb +0 -0
- /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/foo.rb +0 -0
- /data/lib/cov_loupe/{config_parser.rb → config/config_parser.rb} +0 -0
- /data/lib/cov_loupe/{predicate_evaluator.rb → config/predicate_evaluator.rb} +0 -0
- /data/lib/cov_loupe/{error_handler_factory.rb → errors/error_handler_factory.rb} +0 -0
data/docs/user/ERROR_HANDLING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Error Handling Guide
|
|
2
2
|
|
|
3
|
-
[Back to main README](../
|
|
3
|
+
[Back to main README](../index.md)
|
|
4
4
|
|
|
5
5
|
Error handling differs by usage mode:
|
|
6
6
|
|
|
@@ -72,9 +72,9 @@ end
|
|
|
72
72
|
|
|
73
73
|
## Stale Coverage Errors
|
|
74
74
|
|
|
75
|
-
When strict staleness checking is enabled (`--
|
|
75
|
+
When strict staleness checking is enabled (`--raise-on-stale`), the model (and CLI) raise a `CoverageDataStaleError` if a source file appears newer than the coverage data or the line counts differ.
|
|
76
76
|
|
|
77
|
-
- Enable per instance: `CovLoupe::CoverageModel.new(
|
|
77
|
+
- Enable per instance: `CovLoupe::CoverageModel.new(raise_on_stale: true)`
|
|
78
78
|
|
|
79
79
|
The error message is detailed and includes:
|
|
80
80
|
|
|
@@ -89,5 +89,5 @@ Coverage data stale: Coverage data appears stale for lib/foo.rb
|
|
|
89
89
|
File - time: 2025-09-16T14:03:22Z (local 2025-09-16T07:03:22-07:00), lines: 226
|
|
90
90
|
Coverage - time: 2025-09-15T21:11:09Z (local 2025-09-15T14:11:09-07:00), lines: 220
|
|
91
91
|
Delta - file is +123s newer than coverage
|
|
92
|
-
Resultset - /path/to/
|
|
92
|
+
Resultset - /path/to/project/coverage/.resultset.json
|
|
93
93
|
```
|
data/docs/user/EXAMPLES.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# Examples and Recipes
|
|
2
2
|
|
|
3
|
-
[Back to main README](../
|
|
3
|
+
[Back to main README](../index.md)
|
|
4
4
|
|
|
5
5
|
Practical examples for common tasks with cov-loupe. Examples are organized by skill level and use case.
|
|
6
6
|
|
|
7
7
|
> For brevity, these examples use `clp`, an alias to the demo fixture with partial coverage:
|
|
8
|
-
>
|
|
8
|
+
>
|
|
9
|
+
> `alias clp='cov-loupe -R docs/fixtures/demo_project' # -R = --root`
|
|
10
|
+
>
|
|
9
11
|
> Swap `clp` for `cov-loupe` to run against your own project and resultset.
|
|
12
|
+
> The demo fixture is a small Rails-like project in `docs/fixtures/demo_project` with intentional coverage gaps for testing `--tracked-globs`.
|
|
10
13
|
|
|
11
14
|
## Table of Contents
|
|
12
15
|
|
|
@@ -22,11 +25,11 @@ Practical examples for common tasks with cov-loupe. Examples are organized by sk
|
|
|
22
25
|
### View All Coverage
|
|
23
26
|
|
|
24
27
|
```bash
|
|
25
|
-
# Default: show all files,
|
|
28
|
+
# Default: show all files, best coverage first
|
|
26
29
|
clp
|
|
27
30
|
|
|
28
|
-
# Show files with
|
|
29
|
-
clp -o
|
|
31
|
+
# Show files with worst coverage first
|
|
32
|
+
clp -o a list # -o = --sort-order, a = ascending
|
|
30
33
|
|
|
31
34
|
# Export to JSON for processing
|
|
32
35
|
clp -fJ list > coverage-report.json
|
|
@@ -48,21 +51,21 @@ clp -s u -c 3 uncovered app/controllers/orders_controller.rb # -s = --source (u
|
|
|
48
51
|
### Find Coverage Gaps
|
|
49
52
|
|
|
50
53
|
```bash
|
|
51
|
-
# Files with worst coverage
|
|
52
|
-
clp list |
|
|
54
|
+
# Files with worst coverage (account for header/footer)
|
|
55
|
+
clp list | tail -12
|
|
53
56
|
|
|
54
57
|
# Only show files below 80%
|
|
55
|
-
clp -fJ list | jq '.files[] | select(.percentage <
|
|
58
|
+
clp -fJ list | jq '.files[] | select(.percentage < 80)'
|
|
56
59
|
|
|
57
60
|
# Ruby alternative:
|
|
58
61
|
clp -fJ list | ruby -r json -e '
|
|
59
|
-
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] <
|
|
62
|
+
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 80 }.each do |f|
|
|
60
63
|
puts JSON.pretty_generate(f)
|
|
61
64
|
end
|
|
62
65
|
'
|
|
63
66
|
|
|
64
67
|
# Rexe alternative:
|
|
65
|
-
clp -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] <
|
|
68
|
+
clp -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] < 80 }'
|
|
66
69
|
|
|
67
70
|
# Check specific directory
|
|
68
71
|
clp -g "lib/payments/**/*.rb" list # -g = --tracked-globs
|
|
@@ -92,18 +95,18 @@ Here are some examples:
|
|
|
92
95
|
**Parse and filter:**
|
|
93
96
|
```bash
|
|
94
97
|
# Files below threshold
|
|
95
|
-
clp -fJ list | jq '.files[] | select(.percentage <
|
|
98
|
+
clp -fJ list | jq '.files[] | select(.percentage < 80) | {file, coverage: .percentage}'
|
|
96
99
|
|
|
97
100
|
# Ruby alternative:
|
|
98
101
|
clp -fJ list | ruby -r json -e '
|
|
99
|
-
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] <
|
|
102
|
+
JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 80 }.each do |f|
|
|
100
103
|
puts JSON.pretty_generate({file: f["file"], coverage: f["percentage"]})
|
|
101
104
|
end
|
|
102
105
|
'
|
|
103
106
|
|
|
104
107
|
# Rexe alternative:
|
|
105
108
|
clp -fJ list | rexe -ij -mb -oJ '
|
|
106
|
-
self["files"].select { |f| f["percentage"] <
|
|
109
|
+
self["files"].select { |f| f["percentage"] < 80 }.map do |f|
|
|
107
110
|
{file: f["file"], coverage: f["percentage"]}
|
|
108
111
|
end
|
|
109
112
|
'
|
|
@@ -199,7 +202,7 @@ model = CovLoupe::CoverageModel.new(root: root)
|
|
|
199
202
|
# Project totals
|
|
200
203
|
totals = model.project_totals
|
|
201
204
|
puts "Total files: #{totals['files']['total']}"
|
|
202
|
-
puts "Average coverage: #{totals['
|
|
205
|
+
puts "Average coverage: #{totals['lines']['percent_covered']}%"
|
|
203
206
|
|
|
204
207
|
# Check specific file
|
|
205
208
|
summary = model.summary_for("app/models/order.rb")
|
|
@@ -217,11 +220,11 @@ require "cov_loupe"
|
|
|
217
220
|
|
|
218
221
|
root = "docs/fixtures/demo_project"
|
|
219
222
|
model = CovLoupe::CoverageModel.new(root: root)
|
|
220
|
-
|
|
223
|
+
list = model.list['files']
|
|
221
224
|
|
|
222
225
|
# Find files below threshold
|
|
223
226
|
THRESHOLD = 80.0
|
|
224
|
-
low_coverage =
|
|
227
|
+
low_coverage = list.select { |f| f['percentage'] < THRESHOLD }
|
|
225
228
|
|
|
226
229
|
if low_coverage.any?
|
|
227
230
|
puts "Files below #{THRESHOLD}%:"
|
|
@@ -235,7 +238,7 @@ dirs = %w[app lib lib/payments lib/ops/jobs].uniq
|
|
|
235
238
|
dirs.each do |dir|
|
|
236
239
|
pattern = File.join(dir, '**/*.rb')
|
|
237
240
|
totals = model.project_totals(tracked_globs: pattern)
|
|
238
|
-
puts "#{dir}: #{totals['
|
|
241
|
+
puts "#{dir}: #{totals['lines']['percent_covered'].round(2)}% (#{totals['files']['total']} files)"
|
|
239
242
|
end
|
|
240
243
|
```
|
|
241
244
|
|
|
@@ -247,11 +250,11 @@ require "pathname"
|
|
|
247
250
|
|
|
248
251
|
root = "docs/fixtures/demo_project"
|
|
249
252
|
model = CovLoupe::CoverageModel.new(root: root)
|
|
250
|
-
|
|
253
|
+
list = model.list['files']
|
|
251
254
|
|
|
252
255
|
# Filter to lib/payments (coverage data stores absolute paths)
|
|
253
256
|
lib_root = File.expand_path("lib/payments", File.expand_path(root, Dir.pwd))
|
|
254
|
-
lib_files =
|
|
257
|
+
lib_files = list.select { |f| f['file'].start_with?(lib_root) }
|
|
255
258
|
|
|
256
259
|
# Generate custom table
|
|
257
260
|
table = model.format_table(lib_files, sort_order: :ascending)
|
|
@@ -269,65 +272,48 @@ end
|
|
|
269
272
|
|
|
270
273
|
### Coverage Analysis
|
|
271
274
|
|
|
272
|
-
|
|
273
|
-
Using cov-loupe, show me a table of all files sorted by coverage percentage.
|
|
274
|
-
```
|
|
275
|
+
"Using cov-loupe, show me a table of all files sorted by coverage percentage."
|
|
275
276
|
|
|
276
|
-
|
|
277
|
-
Using cov-loupe, find the 10 files with the lowest coverage and create a markdown report with:
|
|
277
|
+
"Using cov-loupe, find the 10 files with the lowest coverage and create a markdown report with:
|
|
278
278
|
1. File path
|
|
279
279
|
2. Current coverage %
|
|
280
|
-
3. Number of uncovered lines
|
|
281
|
-
```
|
|
280
|
+
3. Number of uncovered lines"
|
|
282
281
|
|
|
283
|
-
|
|
284
|
-
Using cov-loupe, analyze the lib/payments/ directory and identify which files should be prioritized for additional testing based on coverage gaps and file complexity.
|
|
285
|
-
```
|
|
282
|
+
"Using cov-loupe, analyze the lib/payments/ directory and identify which files should be prioritized for additional testing based on coverage gaps and file complexity."
|
|
286
283
|
|
|
287
284
|
### Finding Specific Issues
|
|
288
285
|
|
|
289
|
-
|
|
290
|
-
Using cov-loupe, show me the uncovered lines in app/controllers/orders_controller.rb with 5 lines of context around each uncovered block.
|
|
291
|
-
```
|
|
286
|
+
"Using cov-loupe, show me the uncovered lines in app/controllers/orders_controller.rb with 5 lines of context around each uncovered block."
|
|
292
287
|
|
|
293
|
-
|
|
294
|
-
Using cov-loupe, find all files in lib/payments/ with less than 80% coverage and list the specific uncovered line numbers for each.
|
|
295
|
-
```
|
|
288
|
+
"Using cov-loupe, find all files in lib/payments/ with less than 80% coverage and list the specific uncovered line numbers for each."
|
|
296
289
|
|
|
297
290
|
### Test Generation
|
|
298
291
|
|
|
299
|
-
|
|
300
|
-
Using cov-loupe, identify the uncovered lines in lib/ops/jobs/report_job.rb and write RSpec tests to cover them.
|
|
301
|
-
```
|
|
292
|
+
"Using cov-loupe, identify the uncovered lines in lib/ops/jobs/report_job.rb and write *meaningful* RSpec tests to cover them."
|
|
302
293
|
|
|
303
|
-
|
|
304
|
-
Using cov-loupe, analyze coverage gaps in the app/controllers/ directory and generate a test plan prioritizing:
|
|
294
|
+
"Using cov-loupe, analyze coverage gaps in the app/controllers/ directory and generate a test plan prioritizing:
|
|
305
295
|
1. Public API methods
|
|
306
296
|
2. Error handling paths
|
|
307
|
-
3. Edge cases
|
|
308
|
-
```
|
|
297
|
+
3. Edge cases"
|
|
309
298
|
|
|
310
299
|
### Reporting
|
|
311
300
|
|
|
312
|
-
|
|
313
|
-
Using cov-loupe, create a coverage report showing:
|
|
301
|
+
"Using cov-loupe, create a coverage report showing:
|
|
314
302
|
- Overall project coverage percentage
|
|
315
303
|
- Top 5 files with worst coverage
|
|
316
304
|
- Recommended next steps to improve coverage
|
|
317
305
|
|
|
318
|
-
Format as markdown.
|
|
319
|
-
```
|
|
306
|
+
Format as markdown."
|
|
320
307
|
|
|
321
308
|
## Test Run Integration
|
|
322
309
|
|
|
323
|
-
### Display Low Coverage Files
|
|
310
|
+
### Display Low Coverage Files
|
|
324
311
|
|
|
325
312
|
Add this to your `spec/spec_helper.rb` to automatically report files below a coverage threshold after each test run:
|
|
326
313
|
|
|
327
314
|
```ruby
|
|
328
315
|
require 'simplecov'
|
|
329
316
|
SimpleCov.start do
|
|
330
|
-
enable_coverage :branch
|
|
331
317
|
add_filter %r{^/spec/}
|
|
332
318
|
track_files 'lib/**/*.rb' # Ensures new/untested files show up with 0%
|
|
333
319
|
end
|
|
@@ -355,29 +341,72 @@ Lowest coverage files (< 80%):
|
|
|
355
341
|
**Parameters:**
|
|
356
342
|
- `threshold:` - Coverage percentage below which files are included (default: 80)
|
|
357
343
|
- `count:` - Maximum number of files to show (default: 5)
|
|
344
|
+
- `root:` - Project root directory (defaults to `SimpleCov.root` when SimpleCov is loaded, otherwise `'.'`)
|
|
345
|
+
- `resultset:` - Path or directory to `.resultset.json` (defaults to `SimpleCov.coverage_dir/.resultset.json` when SimpleCov is loaded)
|
|
346
|
+
- `model:` - Pre-configured `CoverageModel` instance (optional, overrides `root:`/`resultset:`)
|
|
358
347
|
|
|
359
348
|
**Returns:** Formatted string, or `nil` if no files are below the threshold.
|
|
360
349
|
|
|
350
|
+
**SimpleCov Integration:** When SimpleCov is loaded, `CoverageReporter.report` automatically uses SimpleCov's configured root and coverage directory. You can override these by passing explicit `root:` or `resultset:` parameters, or provide a custom `model:` instance.
|
|
351
|
+
|
|
352
|
+
### Custom Coverage Directory
|
|
353
|
+
|
|
354
|
+
If your project uses a custom coverage directory:
|
|
355
|
+
|
|
356
|
+
```ruby
|
|
357
|
+
require 'simplecov'
|
|
358
|
+
SimpleCov.start do
|
|
359
|
+
add_filter %r{^/spec/}
|
|
360
|
+
coverage_dir 'reports/coverage' # Custom coverage directory
|
|
361
|
+
track_files 'lib/**/*.rb'
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
SimpleCov.at_exit do
|
|
365
|
+
SimpleCov.result.format!
|
|
366
|
+
require 'cov_loupe'
|
|
367
|
+
|
|
368
|
+
# CoverageReporter will automatically find the coverage in reports/coverage
|
|
369
|
+
report = CovLoupe::CoverageReporter.report(threshold: 80, count: 5)
|
|
370
|
+
puts report if report
|
|
371
|
+
end
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Or specify the resultset path explicitly:
|
|
375
|
+
|
|
376
|
+
```ruby
|
|
377
|
+
report = CovLoupe::CoverageReporter.report(
|
|
378
|
+
threshold: 80,
|
|
379
|
+
count: 5,
|
|
380
|
+
resultset: 'reports/coverage/.resultset.json'
|
|
381
|
+
)
|
|
382
|
+
```
|
|
383
|
+
|
|
361
384
|
## CI/CD Integration
|
|
362
385
|
|
|
363
386
|
### GitHub Actions
|
|
364
387
|
|
|
365
|
-
**Fail on low coverage:**
|
|
388
|
+
**Fail on low coverage (Cross-Platform):**
|
|
366
389
|
```yaml
|
|
367
390
|
name: Coverage Check
|
|
368
391
|
|
|
369
392
|
on: [push, pull_request]
|
|
370
393
|
|
|
371
394
|
jobs:
|
|
372
|
-
|
|
373
|
-
runs-on:
|
|
395
|
+
test:
|
|
396
|
+
runs-on: ${{ matrix.os }}
|
|
397
|
+
strategy:
|
|
398
|
+
fail-fast: false
|
|
399
|
+
matrix:
|
|
400
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
401
|
+
ruby-version: ['3.4']
|
|
402
|
+
|
|
374
403
|
steps:
|
|
375
|
-
- uses: actions/checkout@
|
|
404
|
+
- uses: actions/checkout@v4
|
|
376
405
|
|
|
377
406
|
- name: Setup Ruby
|
|
378
407
|
uses: ruby/setup-ruby@v1
|
|
379
408
|
with:
|
|
380
|
-
ruby-version:
|
|
409
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
381
410
|
bundler-cache: true
|
|
382
411
|
|
|
383
412
|
- name: Run tests
|
|
@@ -387,63 +416,55 @@ jobs:
|
|
|
387
416
|
run: gem install cov-loupe
|
|
388
417
|
|
|
389
418
|
- name: Check coverage threshold
|
|
419
|
+
shell: bash
|
|
390
420
|
run: |
|
|
391
|
-
clp
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
# Ruby
|
|
395
|
-
#
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
# end
|
|
411
|
-
# '
|
|
412
|
-
|
|
413
|
-
# Rexe alternative:
|
|
414
|
-
# rexe -f coverage.json '
|
|
415
|
-
# self["files"].select { |f| f["percentage"] < 80 }.each do |f|
|
|
416
|
-
# puts "#{f["file"]}: #{f["percentage"]}%"
|
|
417
|
-
# end
|
|
418
|
-
# '
|
|
419
|
-
|
|
420
|
-
exit 1
|
|
421
|
-
fi
|
|
422
|
-
echo "✓ All files meet coverage threshold"
|
|
421
|
+
# Generate JSON report using the full command (aliases like 'clp' are not available here)
|
|
422
|
+
cov-loupe -fJ list > coverage.json
|
|
423
|
+
|
|
424
|
+
# Verify coverage using Ruby for cross-platform compatibility
|
|
425
|
+
# (Tools like jq and rexe are not guaranteed to be installed on all runners)
|
|
426
|
+
ruby -r json -e '
|
|
427
|
+
data = JSON.parse(File.read("coverage.json"))
|
|
428
|
+
files = data["files"]
|
|
429
|
+
low_cov_files = files.select { |f| f["percentage"] < 80 }
|
|
430
|
+
|
|
431
|
+
if low_cov_files.any?
|
|
432
|
+
puts "❌ #{low_cov_files.count} files below 80% coverage:"
|
|
433
|
+
low_cov_files.each do |f|
|
|
434
|
+
puts " #{f["percentage"]}% #{f["file"]}"
|
|
435
|
+
end
|
|
436
|
+
exit 1
|
|
437
|
+
end
|
|
438
|
+
puts "✓ All files meet coverage threshold"
|
|
439
|
+
'
|
|
423
440
|
|
|
424
441
|
- name: Upload coverage report
|
|
425
|
-
|
|
442
|
+
# Saves the coverage file as an artifact so you can download/inspect it
|
|
443
|
+
# from the GitHub Actions run summary page.
|
|
444
|
+
uses: actions/upload-artifact@v4
|
|
445
|
+
if: always()
|
|
426
446
|
with:
|
|
427
|
-
name: coverage-report
|
|
447
|
+
name: coverage-report-${{ matrix.os }}
|
|
428
448
|
path: coverage.json
|
|
429
449
|
```
|
|
430
450
|
|
|
431
451
|
**Check for stale coverage:**
|
|
432
452
|
```yaml
|
|
433
453
|
- name: Verify coverage is fresh
|
|
434
|
-
|
|
454
|
+
shell: bash
|
|
455
|
+
run: cov-loupe --raise-on-stale true list || exit 1
|
|
435
456
|
```
|
|
436
457
|
|
|
437
458
|
### GitLab CI
|
|
438
459
|
|
|
439
460
|
```yaml
|
|
440
461
|
test:
|
|
441
|
-
image: ruby:3.
|
|
462
|
+
image: ruby:3.4
|
|
442
463
|
before_script:
|
|
443
464
|
- gem install cov-loupe
|
|
444
465
|
script:
|
|
445
466
|
- bundle exec rspec
|
|
446
|
-
- cov-loupe --
|
|
467
|
+
- cov-loupe --raise-on-stale true list
|
|
447
468
|
artifacts:
|
|
448
469
|
paths:
|
|
449
470
|
- coverage/
|
|
@@ -458,18 +479,18 @@ test:
|
|
|
458
479
|
```ruby
|
|
459
480
|
# coverage_policy.rb
|
|
460
481
|
->(model) do
|
|
461
|
-
|
|
482
|
+
list = model.list['files']
|
|
462
483
|
|
|
463
484
|
# Must have at least 80% average coverage
|
|
464
485
|
totals = model.project_totals
|
|
465
|
-
return false if totals['
|
|
486
|
+
return false if totals['lines']['percent_covered'] < 80.0
|
|
466
487
|
|
|
467
488
|
# No files below 60%
|
|
468
|
-
return false if
|
|
489
|
+
return false if list.any? { |f| f['percentage'] < 60.0 }
|
|
469
490
|
|
|
470
491
|
# lib/ files must average 90%
|
|
471
492
|
lib_totals = model.project_totals(tracked_globs: ['lib/**/*.rb'])
|
|
472
|
-
return false if lib_totals['
|
|
493
|
+
return false if lib_totals['lines']['percent_covered'] < 90.0
|
|
473
494
|
|
|
474
495
|
true
|
|
475
496
|
end
|
|
@@ -477,7 +498,7 @@ end
|
|
|
477
498
|
|
|
478
499
|
```bash
|
|
479
500
|
# Use in CI
|
|
480
|
-
|
|
501
|
+
cov-loupe validate coverage_policy.rb
|
|
481
502
|
```
|
|
482
503
|
|
|
483
504
|
## Advanced Usage
|
|
@@ -489,7 +510,6 @@ require "cov_loupe"
|
|
|
489
510
|
|
|
490
511
|
root = "docs/fixtures/demo_project"
|
|
491
512
|
model = CovLoupe::CoverageModel.new(root: root)
|
|
492
|
-
all_files = model.all_files
|
|
493
513
|
|
|
494
514
|
# Calculate coverage by directory (uses the same data as `cov-loupe totals`)
|
|
495
515
|
patterns = %w[
|
|
@@ -504,7 +524,7 @@ results = patterns.map do |pattern|
|
|
|
504
524
|
{
|
|
505
525
|
directory: pattern,
|
|
506
526
|
files: totals['files']['total'],
|
|
507
|
-
coverage: totals['
|
|
527
|
+
coverage: totals['lines']['percent_covered'].round(2),
|
|
508
528
|
covered: totals['lines']['covered'],
|
|
509
529
|
total: totals['lines']['total']
|
|
510
530
|
}
|
|
@@ -516,34 +536,6 @@ results.sort_by { |r| r[:coverage] }.each do |r|
|
|
|
516
536
|
end
|
|
517
537
|
```
|
|
518
538
|
|
|
519
|
-
### Coverage Delta Tracking
|
|
520
|
-
|
|
521
|
-
```ruby
|
|
522
|
-
require "cov_loupe"
|
|
523
|
-
require "json"
|
|
524
|
-
|
|
525
|
-
# Save current coverage
|
|
526
|
-
root = "docs/fixtures/demo_project"
|
|
527
|
-
model = CovLoupe::CoverageModel.new(root: root)
|
|
528
|
-
current = model.all_files
|
|
529
|
-
|
|
530
|
-
File.write("coverage_baseline.json", JSON.pretty_generate(current))
|
|
531
|
-
|
|
532
|
-
# Later, compare with baseline
|
|
533
|
-
baseline = JSON.parse(File.read("coverage_baseline.json"))
|
|
534
|
-
|
|
535
|
-
current.each do |file|
|
|
536
|
-
baseline_file = baseline.find { |f| f['file'] == file['file'] }
|
|
537
|
-
next unless baseline_file
|
|
538
|
-
|
|
539
|
-
delta = file['percentage'] - baseline_file['percentage']
|
|
540
|
-
if delta.abs > 0.1 # Changed by more than 0.1%
|
|
541
|
-
symbol = delta > 0 ? '↑' : '↓'
|
|
542
|
-
puts "#{symbol} #{file['file']}: #{baseline_file['percentage']}% → #{file['percentage']}%"
|
|
543
|
-
end
|
|
544
|
-
end
|
|
545
|
-
```
|
|
546
|
-
|
|
547
539
|
### Integration with Code Review
|
|
548
540
|
|
|
549
541
|
```ruby
|
|
@@ -575,10 +567,11 @@ end
|
|
|
575
567
|
|
|
576
568
|
## Example Scripts
|
|
577
569
|
|
|
578
|
-
The
|
|
570
|
+
The `examples/` directory contains runnable scripts:
|
|
579
571
|
|
|
580
|
-
- **
|
|
581
|
-
- **[success_predicates
|
|
572
|
+
- **filter_and_table_demo.rb** - Filter and format coverage data (in `examples/` directory)
|
|
573
|
+
- **[success_predicates](../examples/success_predicates.md)** - Custom coverage policy examples
|
|
574
|
+
- **[Coverage Delta Tracking recipe in the Library API Guide](LIBRARY_API.md#coverage-delta-tracking)**
|
|
582
575
|
|
|
583
576
|
## Related Documentation
|
|
584
577
|
|
data/docs/user/INSTALLATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Installation Guide
|
|
2
2
|
|
|
3
|
-
[Back to main README](../
|
|
3
|
+
[Back to main README](../index.md)
|
|
4
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
@@ -49,33 +49,6 @@ require "cov_loupe"
|
|
|
49
49
|
|
|
50
50
|
The executable is `cov-loupe` (with hyphen).
|
|
51
51
|
|
|
52
|
-
## PATH Configuration
|
|
53
|
-
|
|
54
|
-
### With Version Managers
|
|
55
|
-
|
|
56
|
-
Most version managers (rbenv, asdf, RVM, chruby) automatically configure PATH. After installation:
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
# Refresh shims if needed
|
|
60
|
-
rbenv rehash # rbenv
|
|
61
|
-
asdf reshim ruby # asdf
|
|
62
|
-
|
|
63
|
-
# Verify executable is accessible
|
|
64
|
-
which cov-loupe
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Important:** When changing Ruby versions, reinstall the gem and update any MCP configurations that use absolute paths.
|
|
68
|
-
|
|
69
|
-
### Bundler Execution
|
|
70
|
-
|
|
71
|
-
If PATH setup is problematic, use bundler:
|
|
72
|
-
|
|
73
|
-
```sh
|
|
74
|
-
bundle exec cov-loupe
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This works from any project directory that has cov-loupe in its Gemfile.
|
|
78
|
-
|
|
79
52
|
## Verification
|
|
80
53
|
|
|
81
54
|
### Test Installation
|
|
@@ -122,6 +95,14 @@ Works with system Ruby or any version manager.
|
|
|
122
95
|
|
|
123
96
|
Should work with Ruby installed via RubyInstaller. PATH configuration may differ.
|
|
124
97
|
|
|
98
|
+
## Upgrading
|
|
99
|
+
|
|
100
|
+
If you are upgrading from an earlier version of `cov-loupe`, please check the migration guides:
|
|
101
|
+
|
|
102
|
+
- [Migrating to v4](migrations/MIGRATING_TO_V4.md)
|
|
103
|
+
- [Migrating to v3](migrations/MIGRATING_TO_V3.md)
|
|
104
|
+
- [Migrating to v2](migrations/MIGRATING_TO_V2.md)
|
|
105
|
+
|
|
125
106
|
## Next Steps
|
|
126
107
|
|
|
127
108
|
- **[CLI Usage](CLI_USAGE.md)** - Learn command-line options
|