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/ADVANCED_USAGE.md
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
# Advanced Usage Guide
|
|
2
2
|
|
|
3
|
-
[Back to main README](../
|
|
3
|
+
[Back to main README](../index.md)
|
|
4
4
|
|
|
5
5
|
> Examples use `clp`, an alias pointed at the demo fixture with partial coverage:
|
|
6
|
-
>
|
|
7
|
-
>
|
|
6
|
+
>
|
|
7
|
+
> `alias clp='cov-loupe -R docs/fixtures/demo_project' # (-R = --root_dir)`
|
|
8
|
+
>
|
|
9
|
+
> Replace `clp` with `cov-loupe` if you want to target your own project/resultset.
|
|
8
10
|
|
|
9
11
|
## Table of Contents
|
|
10
12
|
|
|
11
13
|
- [Advanced MCP Integration](#advanced-mcp-integration)
|
|
12
|
-
- [Staleness Detection & Validation](#staleness-detection
|
|
14
|
+
- [Staleness Detection & Validation](#staleness-detection-validation)
|
|
13
15
|
- [Advanced Path Resolution](#advanced-path-resolution)
|
|
14
16
|
- [Error Handling Strategies](#error-handling-strategies)
|
|
15
17
|
- [Custom Ruby Integration](#custom-ruby-integration)
|
|
16
|
-
- [CI/CD Integration
|
|
17
|
-
- [Advanced Filtering & Glob Patterns](#advanced-filtering
|
|
18
|
+
- [CI/CD Integration](#cicd-integration)
|
|
19
|
+
- [Advanced Filtering & Glob Patterns](#advanced-filtering-glob-patterns)
|
|
18
20
|
- [Performance Optimization](#performance-optimization)
|
|
19
21
|
- [Custom Output Processing](#custom-output-processing)
|
|
22
|
+
- [Multi-Suite Coverage Merging](#multi-suite-coverage-merging)
|
|
20
23
|
|
|
21
24
|
---
|
|
22
25
|
|
|
@@ -45,63 +48,74 @@ The MCP server uses structured error responses:
|
|
|
45
48
|
|
|
46
49
|
The MCP server logs to `cov_loupe.log` in the current directory by default.
|
|
47
50
|
|
|
48
|
-
To override the default log file location, specify the `--log-file` argument wherever and however you configure your MCP server. For example, to log to a different file path, include
|
|
51
|
+
To override the default log file location, specify the `--log-file` (or `-l`) argument wherever and however you configure your MCP server. For example, to log to a different file path, include `-l /path/to/logfile.log` in your server configuration. To log to standard error, use `-l stderr`.
|
|
49
52
|
|
|
50
|
-
**
|
|
53
|
+
**Warning:** Log files may grow unbounded in long-running or CI usage. Consider using a log rotation tool or periodically cleaning up the log file if this is a concern.
|
|
54
|
+
|
|
55
|
+
**Note:** Logging to `stdout` is not permitted in MCP mode since it would interfere with the request processing.
|
|
51
56
|
|
|
52
57
|
### Testing MCP Server Manually
|
|
53
58
|
|
|
54
|
-
Use JSON-RPC over stdin to test the MCP server
|
|
59
|
+
Use JSON-RPC over stdin to test the MCP server. **Note:** CLI flags set defaults for MCP tool calls, but per-request JSON parameters still win. Use `-R`/`-r` when you want server-wide defaults, or pass `root`/`resultset` per request.
|
|
55
60
|
|
|
56
61
|
```sh
|
|
57
|
-
# Get version
|
|
58
|
-
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"version_tool","arguments":{}}}' |
|
|
62
|
+
# Get version (no parameters needed)
|
|
63
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"version_tool","arguments":{}}}' | cov-loupe -m mcp
|
|
59
64
|
|
|
60
|
-
# Get file summary
|
|
61
|
-
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"app/models/order.rb"}}}' |
|
|
65
|
+
# Get file summary (include root parameter in JSON)
|
|
66
|
+
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"app/models/order.rb","root":"docs/fixtures/demo_project"}}}' | cov-loupe -m mcp
|
|
62
67
|
|
|
63
|
-
# List all files with sorting
|
|
64
|
-
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"
|
|
68
|
+
# List all files with sorting (include root parameter)
|
|
69
|
+
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_tool","arguments":{"sort_order":"ascending","root":"docs/fixtures/demo_project"}}}' | cov-loupe -m mcp
|
|
65
70
|
|
|
66
|
-
# Get uncovered lines
|
|
67
|
-
echo '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"uncovered_lines_tool","arguments":{"path":"app/controllers/orders_controller.rb"}}}' |
|
|
71
|
+
# Get uncovered lines (include root parameter)
|
|
72
|
+
echo '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"uncovered_lines_tool","arguments":{"path":"app/controllers/orders_controller.rb","root":"docs/fixtures/demo_project"}}}' | cov-loupe -m mcp
|
|
68
73
|
```
|
|
69
74
|
|
|
75
|
+
**Why not use `clp` alias here?** `clp` is useful for CLI subcommands, but MCP calls run a long-lived server process. You can pass `-R` at startup to set defaults, or set `root` explicitly in each JSON request when you want to be explicit or override the defaults.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Sorting Coverage Lists with n/a Entries
|
|
80
|
+
|
|
81
|
+
When a file has no executable lines, its coverage percentage is reported as `n/a`. In list outputs, `n/a` entries are grouped separately from numeric percentages. The default descending sort order places `n/a` entries above `100%` coverage so they appear earlier in the list, while still keeping low numeric percentages toward the bottom for attention.
|
|
82
|
+
|
|
83
|
+
If you need to treat `n/a` differently, post-process the JSON output from `list` or `list_tool` and apply your own sort or filtering rules.
|
|
84
|
+
|
|
70
85
|
---
|
|
71
86
|
|
|
72
87
|
## Staleness Detection & Validation
|
|
73
88
|
|
|
74
|
-
### Understanding Staleness
|
|
89
|
+
### Understanding Staleness Checks
|
|
75
90
|
|
|
76
|
-
Staleness checking prevents using outdated coverage data.
|
|
91
|
+
Staleness checking prevents using outdated coverage data. The behavior is controlled by the boolean `raise_on_stale` setting:
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
93
|
+
**`raise_on_stale: false` (default)**
|
|
94
|
+
- Coverage data is returned even if stale
|
|
95
|
+
- Stale indicators are still computed
|
|
96
|
+
- Best for exploratory reporting
|
|
82
97
|
|
|
83
|
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
- Perfect for CI/CD pipelines
|
|
98
|
+
**`raise_on_stale: true`**
|
|
99
|
+
- Raises errors when stale coverage is detected
|
|
100
|
+
- Recommended for CI/CD enforcement
|
|
87
101
|
|
|
88
102
|
### File-Level Staleness
|
|
89
103
|
|
|
90
104
|
A file is considered stale when any of the following are true:
|
|
91
|
-
1. Source file modified after coverage generation
|
|
105
|
+
1. Source file modified after coverage generation (requires timestamps - see [Timestamp Warnings](#timestamp-warnings))
|
|
92
106
|
2. Line count differs from coverage array length
|
|
93
107
|
3. File exists in coverage but deleted from filesystem
|
|
94
108
|
|
|
95
109
|
**CLI Usage:**
|
|
96
110
|
```sh
|
|
97
|
-
# Fail if
|
|
98
|
-
clp
|
|
111
|
+
# Fail if the file is stale
|
|
112
|
+
clp -S true summary app/models/order.rb # -S = --raise-on-stale
|
|
99
113
|
```
|
|
100
114
|
|
|
101
115
|
**Ruby API:**
|
|
102
116
|
```ruby
|
|
103
117
|
model = CovLoupe::CoverageModel.new(
|
|
104
|
-
|
|
118
|
+
raise_on_stale: true
|
|
105
119
|
)
|
|
106
120
|
|
|
107
121
|
begin
|
|
@@ -119,30 +133,31 @@ end
|
|
|
119
133
|
Detects system-wide staleness issues:
|
|
120
134
|
|
|
121
135
|
**Conditions Checked:**
|
|
122
|
-
1. **Newer files** - Any tracked file modified after coverage
|
|
136
|
+
1. **Newer files** - Any tracked file modified after coverage (requires timestamps - see [Timestamp Warnings](#timestamp-warnings))
|
|
123
137
|
2. **Missing files** - Tracked files with no coverage data
|
|
124
138
|
3. **Deleted files** - Coverage exists for non-existent files
|
|
125
139
|
|
|
126
140
|
**CLI Usage:**
|
|
127
|
-
```sh
|
|
128
|
-
# Track specific patterns
|
|
129
|
-
clp --staleness error \
|
|
130
|
-
-g "lib/payments/**/*.rb" \
|
|
131
|
-
-g "lib/ops/jobs/**/*.rb" # -g = --tracked-globs
|
|
132
141
|
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
You can see if _any_ files in the project are stale by running the (implicit here) `list` command
|
|
143
|
+
with `--raise-on-stale` and checking the exit code:
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
$ cov-loupe -S true list
|
|
147
|
+
Coverage data stale (project): CovLoupe::CoverageDataProjectStaleError
|
|
148
|
+
Coverage - time: 2025-12-10T18:23:00Z (local 2025-12-11T02:23:00+08:00)
|
|
149
|
+
Newer files (1): - lib/cov_loupe/version.rb
|
|
150
|
+
Resultset - /path/to/project/coverage/.resultset.json
|
|
151
|
+
$ echo $?
|
|
152
|
+
1
|
|
135
153
|
```
|
|
136
154
|
|
|
137
155
|
**Ruby API:**
|
|
138
156
|
```ruby
|
|
139
|
-
model = CovLoupe::CoverageModel.new(
|
|
140
|
-
staleness: 'error',
|
|
141
|
-
tracked_globs: ['lib/payments/**/*.rb', 'lib/ops/jobs/**/*.rb']
|
|
142
|
-
)
|
|
157
|
+
model = CovLoupe::CoverageModel.new(raise_on_stale: true)
|
|
143
158
|
|
|
144
159
|
begin
|
|
145
|
-
|
|
160
|
+
model.list(raise_on_stale: true)
|
|
146
161
|
rescue CovLoupe::CoverageDataProjectStaleError => e
|
|
147
162
|
puts "Newer files: #{e.newer_files.join(', ')}"
|
|
148
163
|
puts "Missing from coverage: #{e.missing_files.join(', ')}"
|
|
@@ -150,39 +165,62 @@ rescue CovLoupe::CoverageDataProjectStaleError => e
|
|
|
150
165
|
end
|
|
151
166
|
```
|
|
152
167
|
|
|
153
|
-
###
|
|
168
|
+
### Timestamp Warnings
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
When coverage data lacks timestamps (e.g., manually created resultsets or older SimpleCov versions), cov-loupe displays a warning in both CLI and MCP modes:
|
|
156
171
|
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
```
|
|
173
|
+
WARNING: Coverage timestamps are missing. Time-based staleness checks were skipped.
|
|
174
|
+
Files may appear "ok" even if source code is newer than the coverage data.
|
|
175
|
+
Check your coverage tool configuration to ensure timestamps are recorded.
|
|
176
|
+
```
|
|
160
177
|
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
**What this means:**
|
|
179
|
+
- Time-based staleness checks (the `"newer"` indicator) cannot run without timestamps
|
|
180
|
+
- Files modified after coverage collection won't be flagged as stale
|
|
181
|
+
- Only line count mismatches and missing files will be detected
|
|
182
|
+
- The `timestamp_status` field in JSON output will show `"missing"` instead of `"ok"`
|
|
163
183
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
184
|
+
**Where it appears:**
|
|
185
|
+
- **CLI table format:** After the coverage table in `list` output
|
|
186
|
+
- **CLI JSON format:** After JSON output, and in the `timestamp_status` field
|
|
187
|
+
- **MCP mode:** In `coverage_table_tool` output after the exclusions summary
|
|
188
|
+
- **MCP JSON:** In the `timestamp_status` field of `list_tool` responses
|
|
189
|
+
|
|
190
|
+
**How to fix:**
|
|
191
|
+
|
|
192
|
+
Modern SimpleCov versions automatically include timestamps in `.resultset.json`. If you see this warning:
|
|
167
193
|
|
|
168
|
-
|
|
194
|
+
1. Ensure SimpleCov is up to date (`gem update simplecov`)
|
|
195
|
+
2. Regenerate coverage data (`bundle exec rspec`)
|
|
196
|
+
3. If using custom resultset generation, ensure timestamps are included
|
|
197
|
+
|
|
198
|
+
**Example timestamp in `.resultset.json`:**
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"RSpec": {
|
|
202
|
+
"coverage": { ... },
|
|
203
|
+
"timestamp": 1704067200
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
169
207
|
|
|
170
208
|
---
|
|
171
209
|
|
|
172
210
|
## Advanced Path Resolution
|
|
173
211
|
|
|
174
|
-
###
|
|
212
|
+
### Path Matching Strategy
|
|
175
213
|
|
|
176
|
-
Path resolution order:
|
|
214
|
+
Path resolution uses two strategies in order:
|
|
177
215
|
|
|
178
|
-
1. **Exact absolute path match**
|
|
179
|
-
2. **Relative path resolution
|
|
216
|
+
1. **Exact absolute path match** - Direct lookup using the full path
|
|
217
|
+
2. **Relative path resolution** - Strips project root and retries with relative path
|
|
180
218
|
|
|
181
219
|
```ruby
|
|
182
|
-
model = CovLoupe::CoverageModel.new(root: '/project')
|
|
220
|
+
model = CovLoupe::CoverageModel.new(root: '/path/to/project')
|
|
183
221
|
|
|
184
|
-
model.summary_for('/project/app/models/order.rb') # Absolute
|
|
185
|
-
model.summary_for('app/models/order.rb')
|
|
222
|
+
model.summary_for('/path/to/project/app/models/order.rb') # Absolute
|
|
223
|
+
model.summary_for('app/models/order.rb') # Relative
|
|
186
224
|
```
|
|
187
225
|
|
|
188
226
|
### Working with Multiple Projects
|
|
@@ -190,19 +228,19 @@ model.summary_for('app/models/order.rb') # Relative
|
|
|
190
228
|
```ruby
|
|
191
229
|
# Project A
|
|
192
230
|
model_a = CovLoupe::CoverageModel.new(
|
|
193
|
-
root: '/projects/service-a',
|
|
194
|
-
resultset: '/projects/service-a/coverage/.resultset.json'
|
|
231
|
+
root: '/path/to/projects/service-a',
|
|
232
|
+
resultset: '/path/to/projects/service-a/coverage/.resultset.json'
|
|
195
233
|
)
|
|
196
234
|
|
|
197
235
|
# Project B
|
|
198
236
|
model_b = CovLoupe::CoverageModel.new(
|
|
199
|
-
root: '/projects/service-b',
|
|
200
|
-
resultset: '/projects/service-b/tmp/coverage/.resultset.json'
|
|
237
|
+
root: '/path/to/projects/service-b',
|
|
238
|
+
resultset: '/path/to/projects/service-b/tmp/coverage/.resultset.json'
|
|
201
239
|
)
|
|
202
240
|
|
|
203
241
|
# Compare coverage
|
|
204
|
-
coverage_a = model_a.
|
|
205
|
-
coverage_b = model_b.
|
|
242
|
+
coverage_a = model_a.list
|
|
243
|
+
coverage_b = model_b.list
|
|
206
244
|
```
|
|
207
245
|
|
|
208
246
|
|
|
@@ -240,7 +278,7 @@ require 'cov_loupe'
|
|
|
240
278
|
|
|
241
279
|
begin
|
|
242
280
|
model = CovLoupe::CoverageModel.new(
|
|
243
|
-
root: '/project',
|
|
281
|
+
root: '/path/to/project',
|
|
244
282
|
resultset: '/nonexistent/.resultset.json'
|
|
245
283
|
)
|
|
246
284
|
rescue CovLoupe::FileError => e
|
|
@@ -276,7 +314,13 @@ cli = CovLoupe::CoverageCLI.new(error_handler: CustomErrorHandler.new)
|
|
|
276
314
|
|
|
277
315
|
### Building Custom Coverage Policies
|
|
278
316
|
|
|
279
|
-
Use the `validate` subcommand to enforce custom coverage policies in CI/CD.
|
|
317
|
+
Use the `validate` subcommand to enforce custom coverage policies in CI/CD.
|
|
318
|
+
Example predicates are in [`examples/success_predicates/`](../examples/success_predicates.md).
|
|
319
|
+
|
|
320
|
+
The predicate can be any Ruby object that responds to `call` and accepts a `CoverageModel`
|
|
321
|
+
as its argument. This is usually a lambda (proc), but it can also be a nonlambda proc, a class,
|
|
322
|
+
or an instance with a `call` method. The predicate should return a truthy value for success
|
|
323
|
+
or `false`/`nil` for failure.
|
|
280
324
|
|
|
281
325
|
> **⚠️ SECURITY WARNING**
|
|
282
326
|
>
|
|
@@ -295,7 +339,7 @@ Use the `validate` subcommand to enforce custom coverage policies in CI/CD. Exam
|
|
|
295
339
|
**Quick Usage:**
|
|
296
340
|
```sh
|
|
297
341
|
# All files must be >= 80%
|
|
298
|
-
clp validate examples/success_predicates/
|
|
342
|
+
clp validate examples/success_predicates/list_above_threshold_predicate.rb
|
|
299
343
|
|
|
300
344
|
# Total project coverage >= 85%
|
|
301
345
|
clp validate examples/success_predicates/project_coverage_minimum_predicate.rb
|
|
@@ -304,7 +348,7 @@ clp validate examples/success_predicates/project_coverage_minimum_predicate.rb
|
|
|
304
348
|
clp validate coverage_policy.rb
|
|
305
349
|
|
|
306
350
|
# Inline string mode
|
|
307
|
-
clp validate -i '->(m) { m.
|
|
351
|
+
clp validate -i '->(m) { m.list["files"].all? { |f| f["percentage"] >= 80 } }'
|
|
308
352
|
```
|
|
309
353
|
|
|
310
354
|
**Creating a predicate:**
|
|
@@ -312,7 +356,7 @@ clp validate -i '->(m) { m.all_files.all? { |f| f["percentage"] >= 80 } }'
|
|
|
312
356
|
# coverage_policy.rb
|
|
313
357
|
->(model) do
|
|
314
358
|
# All files must have >= 80% coverage
|
|
315
|
-
model.
|
|
359
|
+
model.list['files'].all? { |f| f['percentage'] >= 80 }
|
|
316
360
|
end
|
|
317
361
|
```
|
|
318
362
|
|
|
@@ -323,7 +367,7 @@ end
|
|
|
323
367
|
class CoveragePolicy
|
|
324
368
|
def call(model)
|
|
325
369
|
threshold = 80
|
|
326
|
-
low_files = model.
|
|
370
|
+
low_files = model.list['files'].select { |f| f['percentage'] < threshold }
|
|
327
371
|
|
|
328
372
|
if low_files.empty?
|
|
329
373
|
puts "✓ All files have >= #{threshold}% coverage"
|
|
@@ -344,26 +388,27 @@ CoveragePolicy.new
|
|
|
344
388
|
- `1` - Predicate returned falsy (fail)
|
|
345
389
|
- `2` - Predicate raised an error
|
|
346
390
|
|
|
347
|
-
See [examples/success_predicates/README.md](
|
|
391
|
+
See [examples/success_predicates/README.md](../examples/success_predicates.md) for more examples.
|
|
348
392
|
|
|
349
393
|
### Path Relativization
|
|
350
394
|
|
|
351
395
|
Convert absolute paths to relative for cleaner output:
|
|
352
396
|
|
|
353
397
|
```ruby
|
|
354
|
-
model = CovLoupe::CoverageModel.new(root: '/project')
|
|
398
|
+
model = CovLoupe::CoverageModel.new(root: '/path/to/project')
|
|
355
399
|
|
|
356
400
|
# Get data with absolute paths
|
|
357
401
|
data = model.summary_for('app/models/order.rb')
|
|
358
|
-
# => { 'file' => '/project/app/models/order.rb', ... }
|
|
402
|
+
# => { 'file' => '/path/to/project/app/models/order.rb', ... }
|
|
359
403
|
|
|
360
404
|
# Relativize paths
|
|
361
405
|
relative_data = model.relativize(data)
|
|
362
406
|
# => { 'file' => 'app/models/order.rb', ... }
|
|
363
407
|
|
|
364
|
-
# Works with
|
|
365
|
-
|
|
366
|
-
|
|
408
|
+
# Works with list payloads too
|
|
409
|
+
list_result = model.list
|
|
410
|
+
relative_list = model.relativize(list_result)
|
|
411
|
+
relative_files = relative_list['files']
|
|
367
412
|
```
|
|
368
413
|
|
|
369
414
|
---
|
|
@@ -376,7 +421,7 @@ The CLI is designed for CI/CD use with features that integrate naturally into pi
|
|
|
376
421
|
|
|
377
422
|
- **Exit codes**: Non-zero on failure, making it suitable for pipeline failure conditions
|
|
378
423
|
- **JSON output**: `-fJ` format for parsing by CI tools and custom processing
|
|
379
|
-
- **Staleness checking**: `--stale
|
|
424
|
+
- **Staleness checking**: `--raise-on-stale true` to fail on outdated coverage data
|
|
380
425
|
- **Success predicates**: Custom Ruby policies for coverage enforcement
|
|
381
426
|
|
|
382
427
|
### Basic CI Pattern
|
|
@@ -386,7 +431,7 @@ The CLI is designed for CI/CD use with features that integrate naturally into pi
|
|
|
386
431
|
bundle exec rspec
|
|
387
432
|
|
|
388
433
|
# 2. Validate coverage freshness (fails with exit code 1 if stale)
|
|
389
|
-
clp
|
|
434
|
+
clp -S true -g "lib/**/*.rb"
|
|
390
435
|
|
|
391
436
|
# 3. Export data for CI artifacts or further processing
|
|
392
437
|
clp -fJ list > coverage.json
|
|
@@ -419,37 +464,67 @@ For platform-specific integration examples (GitHub Actions, GitLab CI, Jenkins,
|
|
|
419
464
|
|
|
420
465
|
### Tracked Globs Overview
|
|
421
466
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
467
|
+
**Default behavior:** By default, `--tracked-globs` is empty (`[]`), which means all files in the coverage resultset are shown. This ensures transparency—you see exactly what SimpleCov measured without any filtering.
|
|
468
|
+
|
|
469
|
+
**Why opt-in filtering?**
|
|
470
|
+
- **Coverage results are not hidden** - Results are not excluded because their filespecs did not match default tracked globs
|
|
471
|
+
- **Meaningful validation** - `missing_tracked_files` only flags files you explicitly expect to have coverage
|
|
472
|
+
- **Project flexibility** - Different projects use different directory structures
|
|
473
|
+
|
|
474
|
+
**Important:** Files lacking any coverage at all (not loaded during tests) will not appear in the resultset and therefore won't be visible with the default empty array. To detect such files, you must set `--tracked-globs` to match the files you expect to have coverage.
|
|
475
|
+
|
|
476
|
+
**Two purposes of tracked globs:**
|
|
477
|
+
1. **Exclude unwanted results** - Only show files from the resultset that match the patterns
|
|
478
|
+
2. **Include files with or without coverage** - Report files that match the patterns but aren't in the resultset (reported in `missing_tracked_files` for `list`, `missing_from_coverage` for `totals`)
|
|
479
|
+
|
|
480
|
+
**Best practice:** Set `COV_LOUPE_OPTS` to match your SimpleCov `track_files` configuration:
|
|
481
|
+
|
|
482
|
+
```ruby
|
|
483
|
+
# spec_helper.rb
|
|
484
|
+
SimpleCov.start do
|
|
485
|
+
add_filter '/spec/'
|
|
486
|
+
track_files 'lib/**/*.rb'
|
|
487
|
+
track_files 'app/**/*.rb'
|
|
488
|
+
end
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
```sh
|
|
492
|
+
# Shell config (.bashrc, .zshrc, etc.)
|
|
493
|
+
export COV_LOUPE_OPTS="--tracked-globs lib/**/*.rb,app/**/*.rb"
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
This alignment ensures:
|
|
497
|
+
- `list` and `totals` output matches SimpleCov's scope
|
|
498
|
+
- `missing_tracked_files` (in `list`) reports files that SimpleCov should track but hasn't measured
|
|
499
|
+
- No surprises from default patterns that don't match your project
|
|
425
500
|
|
|
426
501
|
### Pattern Syntax
|
|
427
502
|
|
|
428
503
|
Uses Ruby's `File.fnmatch` with extended glob support:
|
|
429
504
|
|
|
430
505
|
```sh
|
|
431
|
-
# Single directory
|
|
432
|
-
|
|
506
|
+
# Single directory, recursive
|
|
507
|
+
-g "lib/**/*.rb"
|
|
433
508
|
|
|
434
509
|
# Multiple patterns
|
|
435
|
-
|
|
510
|
+
-g "lib/payments/**/*.rb" -g "lib/ops/jobs/**/*.rb"
|
|
436
511
|
|
|
437
|
-
# Exclude patterns (use CLI filtering)
|
|
438
|
-
clp -fJ list | jq '.files[] | select(.file | test("
|
|
512
|
+
# Exclude patterns (use CLI filtering to exclude ops jobs)
|
|
513
|
+
clp -fJ list | jq '.files[] | select(.file | test("ops") | not)'
|
|
439
514
|
|
|
440
515
|
# Ruby alternative:
|
|
441
516
|
clp -fJ list | ruby -r json -e '
|
|
442
|
-
JSON.parse($stdin.read)["files"].reject { |f| f["file"].include?("
|
|
517
|
+
JSON.parse($stdin.read)["files"].reject { |f| f["file"].include?("ops") }.each do |f|
|
|
443
518
|
puts JSON.pretty_generate(f)
|
|
444
519
|
end
|
|
445
520
|
'
|
|
446
521
|
|
|
447
522
|
# Rexe alternative:
|
|
448
|
-
clp -fJ list | rexe -ij -mb -oJ 'self["files"].reject { |f| f["file"].include?("
|
|
523
|
+
clp -fJ list | rexe -ij -mb -oJ 'self["files"].reject { |f| f["file"].include?("ops") }'
|
|
449
524
|
|
|
450
525
|
# Complex patterns
|
|
451
|
-
|
|
452
|
-
|
|
526
|
+
-g "lib/{models,controllers}/**/*.rb"
|
|
527
|
+
-g "app/**/concerns/*.rb"
|
|
453
528
|
```
|
|
454
529
|
|
|
455
530
|
### Use Cases
|
|
@@ -466,7 +541,7 @@ clp -g "lib/domain/**/*.rb" list
|
|
|
466
541
|
**2. Ensure New Files Have Coverage:**
|
|
467
542
|
```sh
|
|
468
543
|
# Fail if any tracked file lacks coverage
|
|
469
|
-
clp
|
|
544
|
+
clp -S true -g "lib/features/**/*.rb"
|
|
470
545
|
```
|
|
471
546
|
|
|
472
547
|
**3. Multi-tier Reporting:**
|
|
@@ -483,22 +558,22 @@ done
|
|
|
483
558
|
model = CovLoupe::CoverageModel.new
|
|
484
559
|
|
|
485
560
|
# Filter files in output
|
|
486
|
-
api_files = model.
|
|
561
|
+
api_files = model.list(
|
|
487
562
|
tracked_globs: ['lib/api/**/*.rb']
|
|
488
|
-
)
|
|
563
|
+
)['files']
|
|
489
564
|
|
|
490
565
|
# Multi-pattern filtering
|
|
491
|
-
core_files = model.
|
|
566
|
+
core_files = model.list(
|
|
492
567
|
tracked_globs: [
|
|
493
568
|
'lib/core/**/*.rb',
|
|
494
569
|
'lib/domain/**/*.rb'
|
|
495
570
|
]
|
|
496
|
-
)
|
|
571
|
+
)['files']
|
|
497
572
|
|
|
498
573
|
# Validate specific subsystems
|
|
499
574
|
begin
|
|
500
|
-
model.
|
|
501
|
-
|
|
575
|
+
model.list(
|
|
576
|
+
raise_on_stale: true,
|
|
502
577
|
tracked_globs: ['lib/critical/**/*.rb']
|
|
503
578
|
)
|
|
504
579
|
rescue CovLoupe::CoverageDataProjectStaleError => e
|
|
@@ -519,13 +594,13 @@ The `CoverageModel` reads `.resultset.json` once at initialization:
|
|
|
519
594
|
```ruby
|
|
520
595
|
# Good: Single model for multiple queries
|
|
521
596
|
model = CovLoupe::CoverageModel.new
|
|
522
|
-
files = model.
|
|
597
|
+
files = model.list['files']
|
|
523
598
|
file1 = model.summary_for('lib/a.rb')
|
|
524
599
|
file2 = model.summary_for('lib/b.rb')
|
|
525
600
|
|
|
526
601
|
# Bad: Re-reads coverage for each operation
|
|
527
602
|
model1 = CovLoupe::CoverageModel.new
|
|
528
|
-
files = model1.
|
|
603
|
+
files = model1.list['files']
|
|
529
604
|
|
|
530
605
|
model2 = CovLoupe::CoverageModel.new
|
|
531
606
|
file1 = model2.summary_for('lib/a.rb')
|
|
@@ -554,13 +629,13 @@ Use `tracked_globs` to reduce data processing:
|
|
|
554
629
|
|
|
555
630
|
```ruby
|
|
556
631
|
# Bad: Filter after loading all data
|
|
557
|
-
|
|
558
|
-
api_files =
|
|
632
|
+
list = model.list['files']
|
|
633
|
+
api_files = list.select { |f| f['file'].include?('api') }
|
|
559
634
|
|
|
560
635
|
# Good: Filter during query
|
|
561
|
-
api_files = model.
|
|
636
|
+
api_files = model.list(
|
|
562
637
|
tracked_globs: ['lib/api/**/*.rb']
|
|
563
|
-
)
|
|
638
|
+
)['files']
|
|
564
639
|
```
|
|
565
640
|
|
|
566
641
|
### Caching Coverage Models
|
|
@@ -591,7 +666,7 @@ class CoverageCache
|
|
|
591
666
|
end
|
|
592
667
|
|
|
593
668
|
cache = CoverageCache.new
|
|
594
|
-
model = cache.model_for('/project')
|
|
669
|
+
model = cache.model_for('/path/to/project')
|
|
595
670
|
```
|
|
596
671
|
|
|
597
672
|
---
|
|
@@ -605,7 +680,7 @@ model = cache.model_for('/project')
|
|
|
605
680
|
require 'csv'
|
|
606
681
|
|
|
607
682
|
model = CovLoupe::CoverageModel.new
|
|
608
|
-
files = model.
|
|
683
|
+
files = model.list['files']
|
|
609
684
|
|
|
610
685
|
CSV.open('coverage.csv', 'w') do |csv|
|
|
611
686
|
csv << ['File', 'Coverage %', 'Lines Covered', 'Total Lines', 'Stale']
|
|
@@ -648,7 +723,9 @@ template = ERB.new(<<~HTML)
|
|
|
648
723
|
HTML
|
|
649
724
|
|
|
650
725
|
model = CovLoupe::CoverageModel.new
|
|
651
|
-
|
|
726
|
+
list_result = model.list
|
|
727
|
+
relative_list = model.relativize(list_result)
|
|
728
|
+
files = relative_list['files']
|
|
652
729
|
File.write('coverage.html', template.result(binding))
|
|
653
730
|
```
|
|
654
731
|
|
|
@@ -659,13 +736,13 @@ The CLI supports annotated source viewing:
|
|
|
659
736
|
```sh
|
|
660
737
|
# Show uncovered lines with context
|
|
661
738
|
clp uncovered app/models/order.rb \
|
|
662
|
-
|
|
663
|
-
--source-context
|
|
739
|
+
-s uncovered \
|
|
740
|
+
-c 3 # -s = --source, -c = --context-lines
|
|
664
741
|
|
|
665
742
|
# Show full file with coverage annotations
|
|
666
743
|
clp uncovered app/models/order.rb \
|
|
667
|
-
|
|
668
|
-
|
|
744
|
+
-s full \
|
|
745
|
+
-c 0
|
|
669
746
|
```
|
|
670
747
|
|
|
671
748
|
**Programmatic Source Annotation:**
|
|
@@ -748,7 +825,7 @@ require 'net/http'
|
|
|
748
825
|
require 'json'
|
|
749
826
|
|
|
750
827
|
model = CovLoupe::CoverageModel.new
|
|
751
|
-
files = model.
|
|
828
|
+
files = model.list['files']
|
|
752
829
|
|
|
753
830
|
coveralls_data = {
|
|
754
831
|
repo_token: ENV['COVERALLS_REPO_TOKEN'],
|
|
@@ -768,6 +845,22 @@ Net::HTTP.post(uri, coveralls_data.to_json, {
|
|
|
768
845
|
|
|
769
846
|
---
|
|
770
847
|
|
|
848
|
+
## Multi-Suite Coverage Merging
|
|
849
|
+
|
|
850
|
+
### How It Works
|
|
851
|
+
|
|
852
|
+
When a `.resultset.json` file contains multiple test suites (e.g., RSpec + Cucumber), `cov-loupe` automatically merges them using SimpleCov's combine logic. All covered files from every suite become available to the CLI, library, and MCP tools.
|
|
853
|
+
|
|
854
|
+
**Performance:** Single-suite projects avoid loading SimpleCov at runtime. Multi-suite resultsets trigger a lazy SimpleCov load only when needed, keeping the tool fast for the simpler coverage configurations.
|
|
855
|
+
|
|
856
|
+
### Current Limitations
|
|
857
|
+
|
|
858
|
+
**Staleness checks:** When suites are merged, we keep a single "latest suite" timestamp. This matches prior behavior but may under-report stale files if only some suites were re-run after a change. Use `--raise-on-stale` (or `-S`) on the CLI, `raise_on_stale: true` via the Ruby API, or the MCP tool parameter to turn these warnings into hard failures. A per-file timestamp refinement is planned; until then, treat multi-suite staleness flags as advisory rather than definitive.
|
|
859
|
+
|
|
860
|
+
**Multiple resultset files:** Only suites stored inside a *single* `.resultset.json` are merged automatically. If your project produces separate resultset files (e.g., different CI jobs writing `coverage/job1/.resultset.json`, `coverage/job2/.resultset.json`), you must merge them yourself before pointing `cov-loupe` at the combined file.
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
771
864
|
## Additional Resources
|
|
772
865
|
|
|
773
866
|
- [CLI Usage Guide](CLI_USAGE.md)
|