chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:01:57 +08:00
commit 9dda3e2451
399 changed files with 118131 additions and 0 deletions
@@ -0,0 +1,8 @@
{
"GlobalSettings": {
"isCrossAccountBackupEnabled": "false",
"isDelegatedAdministratorEnabled": "false",
"isMpaEnabled": "false"
},
"LastUpdateTime": "2026-05-28T09:52:17.525000+02:00"
}
+11
View File
@@ -0,0 +1,11 @@
Determining projects to restore...
All projects are up-to-date for restore.
/private/tmp/RtkDotnetSmoke/Broken.cs(7,17): error CS1525: Invalid expression term ';' [/private/tmp/RtkDotnetSmoke/RtkDotnetSmoke.csproj]
Build FAILED.
/private/tmp/RtkDotnetSmoke/Broken.cs(7,17): error CS1525: Invalid expression term ';' [/private/tmp/RtkDotnetSmoke/RtkDotnetSmoke.csproj]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.76
+31
View File
@@ -0,0 +1,31 @@
[
{
"FileName": "Program.cs",
"FilePath": "src/Program.cs",
"FileChanges": [
{
"LineNumber": 42,
"CharNumber": 17,
"DiagnosticId": "WHITESPACE",
"FormatDescription": "Fix whitespace"
}
]
},
{
"FileName": "Utils.cs",
"FilePath": "src/Utils.cs",
"FileChanges": [
{
"LineNumber": 15,
"CharNumber": 8,
"DiagnosticId": "IDE0055",
"FormatDescription": "Fix formatting"
}
]
},
{
"FileName": "Tests.cs",
"FilePath": "tests/Tests.cs",
"FileChanges": []
}
]
+1
View File
@@ -0,0 +1 @@
[]
+12
View File
@@ -0,0 +1,12 @@
[
{
"FileName": "Program.cs",
"FilePath": "src/Program.cs",
"FileChanges": []
},
{
"FileName": "Utils.cs",
"FilePath": "src/Utils.cs",
"FileChanges": []
}
]
+18
View File
@@ -0,0 +1,18 @@
Determining projects to restore...
All projects are up-to-date for restore.
RtkDotnetSmoke -> /private/tmp/RtkDotnetSmoke/bin/Debug/net10.0/RtkDotnetSmoke.dll
Test run for /private/tmp/RtkDotnetSmoke/bin/Debug/net10.0/RtkDotnetSmoke.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.11] RtkDotnetSmoke.UnitTest1.Test1 [FAIL]
Failed RtkDotnetSmoke.UnitTest1.Test1 [4 ms]
Error Message:
Assert.Equal() Failure: Values differ
Expected: 2
Actual: 3
Stack Trace:
at RtkDotnetSmoke.UnitTest1.Test1() in /private/tmp/RtkDotnetSmoke/UnitTest1.cs:line 8
Failed! - Failed: 1, Passed: 0, Skipped: 0, Total: 1, Duration: 13 ms - RtkDotnetSmoke.dll (net10.0)
+54
View File
@@ -0,0 +1,54 @@
section_start:1711234567:prepare_executor[0K
Running with gitlab-runner 16.9.0 (656c1943)
on runner-abc123 system ID: r_defGHI456
Using Docker executor with image node:20-alpine ...
Preparing the "docker" executor
Using Docker executor with image node:20-alpine ...
Running on runner-abc123-project-42-concurrent-0 via runner-host...
section_end:1711234570:prepare_executor[0K
section_start:1711234570:get_sources[0K
Getting source from Git repository
Fetching changes with git depth set to 20...
Initialized empty Git repository in /builds/acme/toolkit/.git/
Created fresh repository.
Checking out abc12345 as main...
Skipping Git submodules setup
section_end:1711234575:get_sources[0K
section_start:1711234575:download_artifacts[0K
Downloading artifacts for build (job_id=98765)...
Downloading artifacts from coordinator... ok host=runner-host id=98765 responseStatus=200 OK token=abc123
section_end:1711234578:download_artifacts[0K
section_start:1711234578:build_script[0K
$ npm ci
added 847 packages in 12s
$ npm run build
> acme-toolkit@3.2.1 build
> tsc && vite build
[36;1mvite v5.2.0[0m building for production...
[32m✓[0m 142 modules transformed.
[33m⚠[0m Some chunks are larger than 500 kB after minification.
dist/index.html 0.45 kB │ gzip: 0.29 kB
dist/assets/main-a1b2c3.js 156.78 kB │ gzip: 48.23 kB
dist/assets/style-d4e5f6.css 12.34 kB │ gzip: 3.45 kB
[32m✓[0m built in 4.56s
$ npm test
> acme-toolkit@3.2.1 test
> vitest run
[32m✓[0m src/utils.test.ts (3 tests) 45ms
[32m✓[0m src/api.test.ts (7 tests) 123ms
[31m✗[0m src/auth.test.ts (2 tests) 67ms
FAIL src/auth.test.ts > validateToken > should reject expired tokens
AssertionError: expected true to be false
at src/auth.test.ts:42:18
Test Files 1 failed | 2 passed | 3 total
Tests 1 failed | 11 passed | 12 total
Duration 0.89s
section_end:1711234600:build_script[0K
section_start:1711234600:upload_artifacts[0K
Uploading artifacts...
Uploading artifacts as "archive" to coordinator... ok id=98765 responseStatus=201 Created token=abc123
section_end:1711234605:upload_artifacts[0K
section_start:1711234605:cleanup_file_variables[0K
Cleaning up project directory and file based variables
section_end:1711234606:cleanup_file_variables[0K
[31;1mERROR: Job failed: exit code 1[0m
+122
View File
@@ -0,0 +1,122 @@
[
{
"iid": 156,
"title": "Support glab CI pipeline filtering",
"state": "opened",
"author": {"username": "alice_dev", "name": "Alice Developer", "id": 42},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/156",
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-05T14:30:00Z",
"labels": ["enhancement", "glab"],
"assignees": [{"username": "alice_dev"}],
"description": "## Request\n\nAdd support for `glab ci` pipeline filtering.\n\n<!-- internal tracking: PROJ-789 -->\n\n### Acceptance Criteria\n- [ ] `rtk glab ci list` shows compact pipeline summary\n- [ ] `rtk glab ci status` shows current pipeline status\n- [ ] Token savings >= 80%\n\n---\n\n[![status](https://img.shields.io/badge/status-in_progress-yellow)](https://example.com)\n"
},
{
"iid": 150,
"title": "rtk cargo test shows full output when no failures",
"state": "opened",
"author": {"username": "bob_report", "name": "Bob Reporter", "id": 100},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/150",
"created_at": "2026-02-28T08:00:00Z",
"updated_at": "2026-03-02T16:00:00Z",
"labels": ["bug", "cargo"],
"assignees": [{"username": "dave_fix"}],
"description": "When all tests pass, `rtk cargo test` still shows verbose compilation output instead of just the summary line.\n\n### Steps to Reproduce\n1. Run `rtk cargo test` in a project with all passing tests\n2. Observe that compiler output is included\n\n### Expected\nOnly show test summary when all tests pass.\n\n### Actual\nFull compiler warnings and test output shown."
},
{
"iid": 145,
"title": "Add Helm CLI support",
"state": "opened",
"author": {"username": "carol_infra", "name": "Carol Infra", "id": 200},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/145",
"created_at": "2026-02-25T12:00:00Z",
"updated_at": "2026-03-04T09:00:00Z",
"labels": ["enhancement", "infra"],
"assignees": [],
"description": "Helm CLI outputs are verbose. Would be great to have RTK support for:\n- `helm list` (compact table)\n- `helm status` (summary only)\n- `helm install/upgrade` (ok confirmation)\n\nSimilar to how `rtk kubectl` works."
},
{
"iid": 140,
"title": "Binary size increased 30% after Python/Go modules",
"state": "opened",
"author": {"username": "eve_perf", "name": "Eve Performance", "id": 300},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/140",
"created_at": "2026-02-20T15:00:00Z",
"updated_at": "2026-02-22T10:00:00Z",
"labels": ["performance", "build"],
"assignees": [{"username": "frank_contrib"}],
"description": "After merging Python and Go support, stripped release binary went from 3.2MB to 4.1MB.\n\nInvestigate if we can:\n- Use feature flags to make modules optional\n- Reduce regex count (share patterns across modules)\n- Review serde usage (maybe avoid full JSON parsing for simple cases)"
},
{
"iid": 135,
"title": "rtk gain --history shows wrong dates on macOS",
"state": "closed",
"author": {"username": "george_mac", "name": "George Mac", "id": 400},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/135",
"created_at": "2026-02-15T09:00:00Z",
"updated_at": "2026-02-18T11:00:00Z",
"labels": ["bug", "macos"],
"assignees": [{"username": "alice_dev"}],
"description": "On macOS, `rtk gain --history` shows dates in UTC instead of local timezone.\n\nFixed in v0.23.1."
},
{
"iid": 130,
"title": "Support TOML-based filter DSL",
"state": "opened",
"author": {"username": "heidi_arch", "name": "Heidi Architect", "id": 500},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/130",
"created_at": "2026-02-10T08:00:00Z",
"updated_at": "2026-02-12T16:00:00Z",
"labels": ["enhancement", "architecture"],
"assignees": [],
"description": "Instead of writing Rust code for each new filter, allow users to define filters in TOML.\n\n```toml\n[[filter]]\ncommand = \"terraform plan\"\npattern = \"^(Plan|Apply|Error):\"\nformat = \"compact\"\n```\n\nThis would make RTK extensible without recompilation."
},
{
"iid": 125,
"title": "Improve error messages for missing commands",
"state": "closed",
"author": {"username": "ivan_docs", "name": "Ivan Writer", "id": 600},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/125",
"created_at": "2026-02-05T14:00:00Z",
"updated_at": "2026-02-06T09:00:00Z",
"labels": ["enhancement", "ux"],
"assignees": [{"username": "ivan_docs"}],
"description": "When the underlying command is not installed (e.g., `rtk glab mr list` without glab), the error message is confusing:\n\n```\nError: Failed to run glab mr list\n```\n\nShould say something like:\n```\nError: glab not found. Install it: https://gitlab.com/gitlab-org/cli\n```"
},
{
"iid": 120,
"title": "Add rtk completion command for shell completions",
"state": "opened",
"author": {"username": "judy_shell", "name": "Judy Shell", "id": 700},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/120",
"created_at": "2026-02-01T11:00:00Z",
"updated_at": "2026-02-03T15:00:00Z",
"labels": ["enhancement", "shell"],
"assignees": [],
"description": "Clap supports generating shell completions via `clap_complete`. Add a `rtk completion bash/zsh/fish` command.\n\nThis would help discoverability of available commands."
},
{
"iid": 115,
"title": "rtk read crashes on binary files",
"state": "closed",
"author": {"username": "karl_refactor", "name": "Karl Refactorer", "id": 800},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/115",
"created_at": "2026-01-28T10:00:00Z",
"updated_at": "2026-01-30T12:00:00Z",
"labels": ["bug", "crash"],
"assignees": [{"username": "dave_fix"}],
"description": "Running `rtk read /path/to/binary.exe` panics with:\n```\nthread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Utf8Error'\n```\n\nShould detect binary files and skip filtering."
},
{
"iid": 110,
"title": "Track savings per project directory",
"state": "opened",
"author": {"username": "lisa_feat", "name": "Lisa Feature", "id": 900},
"web_url": "https://gitlab.example.com/acme/toolkit/-/issues/110",
"created_at": "2026-01-25T09:00:00Z",
"updated_at": "2026-01-27T14:00:00Z",
"labels": ["enhancement", "analytics"],
"assignees": [],
"description": "Currently `rtk gain` shows global stats. It would be useful to see savings broken down by project directory.\n\nProposal: store `cwd` in the tracking database and add `rtk gain --by-project` flag."
}
]
+182
View File
@@ -0,0 +1,182 @@
[
{
"iid": 314,
"title": "feat(glab): add GitLab CLI (glab) command support",
"state": "opened",
"author": {"username": "alice_dev", "name": "Alice Developer", "id": 42},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/314",
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-05T14:30:00Z",
"source_branch": "feat/glab-support",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["enhancement", "cli"],
"assignees": [{"username": "alice_dev", "name": "Alice Developer"}],
"reviewers": [{"username": "bob_review"}, {"username": "carol_review"}],
"description": "## Summary\n\nAdd GitLab CLI support.\n\n<!-- auto-generated -->\n\n## Changes\n- New module\n- MR/issue/CI filtering\n- Token savings 80-87%\n\n---\n\n[![CI](https://img.shields.io/badge/CI-passing-green)](https://ci.example.com)\n",
"head_pipeline": {"id": 98765, "status": "success", "ref": "feat/glab-support"}
},
{
"iid": 310,
"title": "fix(git): handle merge commits in compact diff",
"state": "merged",
"author": {"username": "dave_fix", "name": "Dave Fixer", "id": 100},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/310",
"created_at": "2026-02-28T08:00:00Z",
"updated_at": "2026-03-02T16:00:00Z",
"source_branch": "fix/merge-commits",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["bug", "git"],
"assignees": [{"username": "dave_fix"}],
"reviewers": [{"username": "eve_review"}],
"description": "Fix handling of merge commits in `compact_diff`. Previously, merge commits were being skipped entirely which lost context.\n\n### Test Plan\n- [x] Unit tests added\n- [x] Manual verification with merge-heavy repos\n",
"head_pipeline": {"id": 98700, "status": "success", "ref": "fix/merge-commits"}
},
{
"iid": 305,
"title": "feat(aws): add AWS CLI module with token-optimized output",
"state": "opened",
"author": {"username": "frank_contrib", "name": "Frank Contributor", "id": 200},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/305",
"created_at": "2026-02-25T12:00:00Z",
"updated_at": "2026-03-04T09:00:00Z",
"source_branch": "feat/aws-cli",
"target_branch": "master",
"merge_status": "cannot_be_merged",
"draft": true,
"labels": ["enhancement", "infra"],
"assignees": [],
"reviewers": [{"username": "grace_review"}, {"username": "heidi_review"}],
"description": "Add AWS CLI support.\n\n![architecture](https://example.com/arch.png)\n\n## Commands\n- `rtk aws s3 ls`\n- `rtk aws ec2 describe-instances`\n- `rtk aws ecs list-services`\n\n## Token Savings\n| Command | Savings |\n|---------|--------|\n| s3 ls | 75% |\n| ec2 describe | 85% |\n| ecs list | 80% |\n",
"head_pipeline": {"id": 98650, "status": "failed", "ref": "feat/aws-cli"}
},
{
"iid": 302,
"title": "chore(master): release 0.24.0",
"state": "merged",
"author": {"username": "release-bot", "name": "Release Bot", "id": 1},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/302",
"created_at": "2026-02-20T00:00:00Z",
"updated_at": "2026-02-20T01:00:00Z",
"source_branch": "release-please--branches--master",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["release"],
"assignees": [],
"reviewers": [],
"description": "## [0.24.0](https://example.com/compare/v0.23.0...v0.24.0)\n\n### Features\n* feat(aws): add AWS CLI module\n* feat(psql): add PostgreSQL module\n\n### Bug Fixes\n* fix(playwright): fix JSON parser\n",
"head_pipeline": {"id": 98600, "status": "success", "ref": "release-please--branches--master"}
},
{
"iid": 298,
"title": "docs: update README with Python and Go command examples",
"state": "merged",
"author": {"username": "ivan_docs", "name": "Ivan Writer", "id": 300},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/298",
"created_at": "2026-02-18T15:00:00Z",
"updated_at": "2026-02-19T10:00:00Z",
"source_branch": "docs/python-go-examples",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["documentation"],
"assignees": [{"username": "ivan_docs"}],
"reviewers": [{"username": "judy_review"}],
"description": "Update README.md with comprehensive examples for:\n- Python commands (ruff, pytest, pip)\n- Go commands (go test, go build, golangci-lint)\n\nAll examples tested manually.",
"head_pipeline": null
},
{
"iid": 295,
"title": "refactor: extract parser module from runner.rs",
"state": "closed",
"author": {"username": "karl_refactor", "name": "Karl Refactorer", "id": 400},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/295",
"created_at": "2026-02-15T09:00:00Z",
"updated_at": "2026-02-16T11:00:00Z",
"source_branch": "refactor/parser-module",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["refactor"],
"assignees": [{"username": "karl_refactor"}],
"reviewers": [],
"description": "Extract parser logic from runner.rs into dedicated parser/ module.\n\n---\n\nThis was superseded by #300 which took a different approach.\n\n***\n",
"head_pipeline": {"id": 98500, "status": "canceled", "ref": "refactor/parser-module"}
},
{
"iid": 290,
"title": "feat(tee): save raw output on failure for LLM re-read",
"state": "merged",
"author": {"username": "lisa_feat", "name": "Lisa Feature", "id": 500},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/290",
"created_at": "2026-02-10T08:00:00Z",
"updated_at": "2026-02-12T16:00:00Z",
"source_branch": "feat/tee-output",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["enhancement"],
"assignees": [{"username": "lisa_feat"}],
"reviewers": [{"username": "mike_review"}],
"description": "## Tee Output Recovery\n\nSave raw unfiltered output on command failure.\nPrint one-line hint so LLMs can re-read instead of re-run.\n\n### Configuration\n```toml\n[tee]\nenabled = true\ndir = \"~/.local/share/rtk/tee\"\nmax_files = 20\nmax_size = 1048576\n```\n",
"head_pipeline": {"id": 98400, "status": "success", "ref": "feat/tee-output"}
},
{
"iid": 285,
"title": "ci: add ARM64 Linux build to release workflow",
"state": "merged",
"author": {"username": "nancy_ci", "name": "Nancy CI", "id": 600},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/285",
"created_at": "2026-02-05T14:00:00Z",
"updated_at": "2026-02-06T09:00:00Z",
"source_branch": "ci/arm64-build",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["ci"],
"assignees": [{"username": "nancy_ci"}],
"reviewers": [{"username": "oscar_review"}],
"description": "Add ARM64 Linux target to the release workflow.\n\n- Uses `cross` for cross-compilation\n- Generates `.deb` and `.rpm` packages\n- Tested on Raspberry Pi 4 and AWS Graviton",
"head_pipeline": {"id": 98300, "status": "success", "ref": "ci/arm64-build"}
},
{
"iid": 280,
"title": "fix(vitest): handle watch mode output gracefully",
"state": "opened",
"author": {"username": "peter_bugfix", "name": "Peter Bugfix", "id": 700},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/280",
"created_at": "2026-02-01T11:00:00Z",
"updated_at": "2026-02-03T15:00:00Z",
"source_branch": "fix/vitest-watch",
"target_branch": "master",
"merge_status": "unchecked",
"draft": false,
"labels": ["bug", "vitest"],
"assignees": [{"username": "peter_bugfix"}],
"reviewers": [],
"description": "When vitest runs in watch mode, output is continuous and doesn't have a clear end marker. This fix detects watch mode and falls back to passthrough.\n\n<!-- TODO: add unit test -->\n",
"head_pipeline": {"id": 98200, "status": "running", "ref": "fix/vitest-watch"}
},
{
"iid": 275,
"title": "feat(discover): add rtk discover command for missed savings analysis",
"state": "merged",
"author": {"username": "quinn_dev", "name": "Quinn Developer", "id": 800},
"web_url": "https://gitlab.example.com/acme/toolkit/-/merge_requests/275",
"created_at": "2026-01-28T10:00:00Z",
"updated_at": "2026-01-30T12:00:00Z",
"source_branch": "feat/discover",
"target_branch": "master",
"merge_status": "can_be_merged",
"draft": false,
"labels": ["enhancement", "analytics"],
"assignees": [{"username": "quinn_dev"}],
"reviewers": [{"username": "rachel_review"}, {"username": "sam_review"}],
"description": "Add `rtk discover` command that scans Claude Code JSONL sessions and reports missed savings opportunities.\n\n## Features\n- Classifies commands as Supported/Unsupported/Ignored\n- Groups by category with estimated token savings\n- Reports top missed commands\n\n## Example\n```\n$ rtk discover\nAnalyzed 1,234 commands across 45 sessions\n\nMissed savings by category:\n Git: 234 commands, ~16,800 tokens\n Cargo: 89 commands, ~7,120 tokens\n```\n",
"head_pipeline": {"id": 98100, "status": "success", "ref": "feat/discover"}
}
]
+13
View File
@@ -0,0 +1,13 @@
Showing 10 releases on acme/toolkit.
Name Tag Created
v3.2.1 v3.2.1 about 2 days ago
v3.2.0 v3.2.0 about 1 week ago
v3.1.0 v3.1.0 about 3 weeks ago
v3.0.0 v3.0.0 about 1 month ago
v2.5.0 v2.5.0 about 3 months ago
v2.4.1 v2.4.1 about 5 months ago
v2.4.0 v2.4.0 about 6 months ago
v2.3.0 v2.3.0 about 9 months ago
v2.2.0 v2.2.0 about 1 year ago
v2.1.0 v2.1.0 about 2 years ago
+30
View File
@@ -0,0 +1,30 @@
Test Release v2.0
alice_dev released this 3 days ago
abc1234 - v2.0.0
## What's Changed
- Added widget support
- Fixed authentication bug
### Contributors
@alice_dev @bob_dev
--------
Image: logo → https://example.com/logo.png
<!-- internal tracking: PROJ-123 -->
ASSETS
There are no assets for this release
SOURCES
https://gitlab.example.com/acme/toolkit/-/archive/v2.0.0/toolkit-v2.0.0.zip
https://gitlab.example.com/acme/toolkit/-/archive/v2.0.0/toolkit-v2.0.0.tar.gz
https://gitlab.example.com/acme/toolkit/-/archive/v2.0.0/toolkit-v2.0.0.tar.bz2
https://gitlab.example.com/acme/toolkit/-/archive/v2.0.0/toolkit-v2.0.0.tar
View this release on GitLab at https://gitlab.example.com/acme/toolkit/-/releases/v2.0.0
+144
View File
@@ -0,0 +1,144 @@
{
"Issues": [
{
"FromLinter": "errcheck",
"Text": "Error return value of `foo` is not checked",
"Severity": "error",
"SourceLines": [
" if err := foo(); err != nil {",
" return err",
" }"
],
"Pos": {
"Filename": "pkg/handler/server.go",
"Line": 42,
"Column": 5,
"Offset": 1024
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "errcheck",
"Text": "Error return value of `bar` is not checked",
"Severity": "error",
"SourceLines": [
" bar()",
" return nil",
"}"
],
"Pos": {
"Filename": "pkg/handler/server.go",
"Line": 55,
"Column": 2,
"Offset": 2048
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "gosimple",
"Text": "S1003: should replace strings.Index with strings.Contains",
"Severity": "warning",
"SourceLines": [
" if strings.Index(s, sub) >= 0 {",
" return true",
" }"
],
"Pos": {
"Filename": "pkg/utils/strings.go",
"Line": 15,
"Column": 2,
"Offset": 512
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "govet",
"Text": "printf: Sprintf format %s has arg of wrong type int",
"Severity": "error",
"SourceLines": [
" fmt.Sprintf(\"%s\", 42)"
],
"Pos": {
"Filename": "cmd/main/main.go",
"Line": 10,
"Column": 3,
"Offset": 256
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "unused",
"Text": "func `unusedHelper` is unused",
"Severity": "warning",
"SourceLines": [
"func unusedHelper() {",
" // implementation",
"}"
],
"Pos": {
"Filename": "internal/helpers.go",
"Line": 100,
"Column": 1,
"Offset": 4096
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "errcheck",
"Text": "Error return value of `close` is not checked",
"Severity": "error",
"SourceLines": [
" defer file.Close()"
],
"Pos": {
"Filename": "pkg/handler/server.go",
"Line": 120,
"Column": 10,
"Offset": 3072
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
},
{
"FromLinter": "gosimple",
"Text": "S1005: should omit nil check",
"Severity": "warning",
"SourceLines": [
" if m != nil {",
" for k, v := range m {",
" process(k, v)",
" }",
" }"
],
"Pos": {
"Filename": "pkg/utils/strings.go",
"Line": 45,
"Column": 1,
"Offset": 1536
},
"Replacement": null,
"ExpectNoLint": false,
"ExpectedNoLintLinter": ""
}
],
"Report": {
"Warnings": [],
"Linters": [
{"Name": "errcheck", "Enabled": true, "EnabledByDefault": true},
{"Name": "gosimple", "Enabled": true, "EnabledByDefault": true},
{"Name": "govet", "Enabled": true, "EnabledByDefault": true},
{"Name": "unused", "Enabled": true, "EnabledByDefault": true}
]
}
}
+24
View File
@@ -0,0 +1,24 @@
> Configure project :app
> Task :app:preBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:compileDebugKotlin FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:compileDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork
> Compilation error. See log for more details
e: /Users/user/MyApp/app/src/main/java/com/example/myapp/MainActivity.kt: (42, 5): Unresolved reference: MyService
e: /Users/user/MyApp/app/src/main/java/com/example/myapp/MainActivity.kt: (56, 17): Type mismatch: inferred type is String but Int was expected
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 12s
2 actionable tasks: 2 executed
+33
View File
@@ -0,0 +1,33 @@
Starting a Gradle Daemon (subsequent builds will be faster)
Daemon will be stopped at the end of the build after running out of JVM memory
> Configure project :app
> Task :app:preBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:compileDebugSources UP-TO-DATE
> Task :app:lintVitalAnalyzeDebug UP-TO-DATE
> Task :app:mergeDebugShaders UP-TO-DATE
> Task :app:compileDebugShaders UP-TO-DATE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs NO-SOURCE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:writeDebugAppMetadata UP-TO-DATE
> Task :app:writeDebugSigningConfigVersions UP-TO-DATE
> Task :app:packageDebug UP-TO-DATE
> Task :app:createDebugApkListingFileRedirect UP-TO-DATE
> Task :app:assembleDebug UP-TO-DATE
BUILD SUCCESSFUL in 3s
28 actionable tasks: 28 up-to-date
+33
View File
@@ -0,0 +1,33 @@
Starting 2 tests on Pixel_6_API_33(AVD) - 13
Installing APK 'app-debug.apk' on 'Pixel_6_API_33(AVD) - 13'...
Installing APK 'app-debug-androidTest.apk' on 'Pixel_6_API_33(AVD) - 13'...
> Task :app:connectedDebugAndroidTest
INSTRUMENTATION_STATUS: class=com.example.myapp.MainActivityTest
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: numtests=2
INSTRUMENTATION_STATUS: stream=
INSTRUMENTATION_STATUS: test=exampleInstrumentedTest
INSTRUMENTATION_STATUS_CODE: 1
INSTRUMENTATION_STATUS: class=com.example.myapp.MainActivityTest
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
INSTRUMENTATION_STATUS: numtests=2
INSTRUMENTATION_STATUS: stream=
.
INSTRUMENTATION_STATUS: test=exampleInstrumentedTest
INSTRUMENTATION_STATUS_CODE: 0
com.example.myapp.MainActivityTest > exampleInstrumentedTest[Pixel_6_API_33(AVD) - 13] PASSED
INSTRUMENTATION_STATUS: class=com.example.myapp.MainActivityTest
INSTRUMENTATION_STATUS: current=2
INSTRUMENTATION_STATUS: test=anotherTest
INSTRUMENTATION_STATUS_CODE: 1
com.example.myapp.MainActivityTest > anotherTest[Pixel_6_API_33(AVD) - 13] PASSED
INSTRUMENTATION_STATUS_CODE: 0
INSTRUMENTATION_RESULT: stream=
Tests run: 2, Failures: 0
INSTRUMENTATION_CODE: -1
BUILD SUCCESSFUL in 45s
3 actionable tasks: 1 executed, 2 up-to-date
+48
View File
@@ -0,0 +1,48 @@
Starting a Gradle Daemon (subsequent builds will be faster)
Daemon will be stopped at the end of the build after running out of JVM memory
> Configure project :app
> Configure project :core
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:compileDebugAidl NO-SOURCE
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:lintVitalAnalyzeDebug UP-TO-DATE
> Task :app:lint
Ran lint on variant debug: 0 issues found
Wrote HTML report to file:///Users/user/MyApp/app/build/reports/lint-results-debug.html
Wrote XML report to file:///Users/user/MyApp/app/build/reports/lint-results-debug.xml
> Task :app:lintDebug
Ran lint on variant debug: 3 issues found
src/main/java/com/example/myapp/MainActivity.kt:45: Error: Format string invalid [StringFormatInvalid]
String.format(getString(R.string.template), arg1, arg2)
^
This format string placeholder index (2) does not correspond to an argument
src/main/java/com/example/myapp/Utils.kt:89: Warning: HardcodedText [HardcodedText]
return "Hello World"
~~~~~~~~~~~~~
src/main/res/layout/activity_main.xml:15: Warning: Missing contentDescription attribute on image [ContentDescription]
<ImageView
Wrote HTML report to file:///Users/user/MyApp/app/build/reports/lint-results-debug.html
Wrote XML report to file:///Users/user/MyApp/app/build/reports/lint-results-debug.xml
BUILD FAILED in 8s
3 actionable tasks: 2 executed, 1 up-to-date
+19
View File
@@ -0,0 +1,19 @@
> Task :app:testDebugUnitTest
com.example.myapp.CalculatorTest > testAddition PASSED
com.example.myapp.CalculatorTest > testSubtraction FAILED
java.lang.AssertionError: expected:<3> but was:<-1>
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.assertEquals(Assert.java:197)
at com.example.myapp.CalculatorTest.testSubtraction(CalculatorTest.kt:25)
com.example.myapp.CalculatorTest > testMultiplication PASSED
com.example.myapp.MainViewModelTest > loadDataSuccess PASSED
com.example.myapp.MainViewModelTest > loadDataError FAILED
kotlin.NotImplementedError: An operation is not implemented: TODO
at com.example.myapp.MainViewModelTest.loadDataError(MainViewModelTest.kt:45)
5 tests completed, 2 failed
There were failing tests. See the report at: file:///Users/user/MyApp/app/build/reports/tests/testDebugUnitTest/index.html
BUILD FAILED in 22s
4 actionable tasks: 1 executed, 3 up-to-date
+12
View File
@@ -0,0 +1,12 @@
> Task :app:testDebugUnitTest
com.example.myapp.CalculatorTest > testAddition PASSED
com.example.myapp.CalculatorTest > testSubtraction PASSED
com.example.myapp.CalculatorTest > testMultiplication PASSED
com.example.myapp.CalculatorTest > testDivision PASSED
com.example.myapp.MainViewModelTest > loadDataSuccess PASSED
com.example.myapp.MainViewModelTest > loadDataError PASSED
6 tests completed, 0 failed
BUILD SUCCESSFUL in 18s
4 actionable tasks: 1 executed, 3 up-to-date
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.rtk</groupId>
<artifactId>multi-module-skeleton</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>child-a</artifactId>
<packaging>jar</packaging>
</project>
@@ -0,0 +1 @@
public class Empty {}
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example.rtk</groupId>
<artifactId>multi-module-skeleton</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>child-b</artifactId>
<packaging>jar</packaging>
</project>
@@ -0,0 +1 @@
public class Empty {}
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.rtk</groupId>
<artifactId>multi-module-skeleton</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>child-a</module>
<module>child-b</module>
</modules>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.rtk</groupId>
<artifactId>multifail-skeleton</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.5</version>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,7 @@
package com.example.rtk;
public class Calc {
public int add(int a, int b) {
return a + b;
}
}
@@ -0,0 +1,10 @@
package com.example.rtk;
import org.junit.jupiter.api.Test;
class BoomTest {
@Test
void boom() {
throw new IllegalStateException("boom: errors-only class for fixture capture");
}
}
@@ -0,0 +1,24 @@
package com.example.rtk;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class CalcTest {
private final Calc calc = new Calc();
@Test
void failOne() {
assertEquals(5, calc.add(2, 2), "failOne: addition should equal five");
}
@Test
void failTwo() {
throw new IllegalStateException("failTwo: induced error for fixture capture");
}
@Test
void passes() {
assertEquals(4, calc.add(2, 2));
}
}
+15
View File
@@ -0,0 +1,15 @@
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< com.example:myapp >------------------------
[INFO] Building myapp 1.0.0
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ myapp ---
[INFO] Deleting /path/to/project/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.853 s
[INFO] Finished at: 2026-01-15T10:30:00Z
[INFO] ------------------------------------------------------------------------
+33
View File
@@ -0,0 +1,33 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< commons-cli:commons-cli >-----------------------
[INFO] Building Apache Commons CLI 1.11.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- compiler:3.15.0:compile (default-compile) @ commons-cli ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 37 source files with javac [debug release 8] to target/classes
[INFO] -------------------------------------------------------------
[WARNING] COMPILATION WARNING :
[INFO] -------------------------------------------------------------
[WARNING] source value 8 is obsolete and will be removed in a future release
[WARNING] target value 8 is obsolete and will be removed in a future release
[WARNING] To suppress warnings about obsolete options, use -Xlint:-options.
[INFO] 3 warnings
[INFO] -------------------------------------------------------------
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] src/main/java/org/apache/commons/cli/CompileBreaker.java:[21,16] cannot find symbol
symbol: variable bar
location: class org.apache.commons.cli.CompileBreaker
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 30.631 s
[INFO] Finished at: 2026-05-21T14:59:46Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.15.0:compile (default-compile) on project commons-cli: Compilation failure
Binary file not shown.
+44
View File
@@ -0,0 +1,44 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< commons-cli:commons-cli >-----------------------
[INFO] Building Apache Commons CLI 1.11.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- compiler:3.15.0:compile (default-compile) @ commons-cli ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 47 source files with javac [debug parameters release 11] to target/classes
[INFO]
[INFO] --- compiler:3.15.0:testCompile (default-testCompile) @ commons-cli ---
[INFO] Recompiling the module because of changed dependency.
[INFO] Compiling 52 source files with javac [debug parameters release 11] to target/test-classes
[INFO]
[INFO] --- surefire:3.5.5:test (default-test) @ commons-cli ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.commons.cli.help.UtilTest
[INFO] Tests run: 34, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.023 s -- in org.apache.commons.cli.help.UtilTest
[INFO] Running org.apache.commons.cli.DefaultParserTest
[WARNING] Tests run: 87, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 1.023 s -- in org.apache.commons.cli.DefaultParserTest
[INFO] Running org.apache.commons.cli.AlreadySelectedExceptionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 s -- in org.apache.commons.cli.AlreadySelectedExceptionTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 977, Failures: 0, Errors: 0, Skipped: 61
[INFO]
[INFO] --- jar:3.5.0:jar (default-jar) @ commons-cli ---
[INFO] Building jar: target/commons-cli-1.11.1-SNAPSHOT.jar
[INFO]
[INFO] --- install:3.1.4:install (default-install) @ commons-cli ---
[INFO] Installing pom.xml to /root/.m2/repository/commons-cli/commons-cli/1.11.1-SNAPSHOT/commons-cli-1.11.1-SNAPSHOT.pom
[INFO] Installing target/commons-cli-1.11.1-SNAPSHOT.jar to /root/.m2/repository/commons-cli/commons-cli/1.11.1-SNAPSHOT/commons-cli-1.11.1-SNAPSHOT.jar
[INFO] Installing target/commons-cli-1.11.1-SNAPSHOT-sources.jar to /root/.m2/repository/commons-cli/commons-cli/1.11.1-SNAPSHOT/commons-cli-1.11.1-SNAPSHOT-sources.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:29 min
[INFO] Finished at: 2026-05-21T15:13:00Z
[INFO] ------------------------------------------------------------------------
+15
View File
@@ -0,0 +1,15 @@
[INFO] Recherche des projets...
[INFO]
[INFO] -----------------------< com.example:myapp >------------------------
[INFO] Construction de myapp 1.0.0
[INFO] de pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- clean:3.2.0:clean (default-clean) @ myapp ---
[INFO] Suppression de /path/to/project/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD ÉCHEC
[INFO] ------------------------------------------------------------------------
[INFO] Temps total: 0.713 s
[INFO] Termine a: 2026-01-15T10:30:00Z
[INFO] ------------------------------------------------------------------------
+8
View File
@@ -0,0 +1,8 @@
[INFO] Scanning for projects...
[ERROR] The goal you specified requires a project to execute but there is no POM in this directory (/tmp). Please verify you invoked Maven from the correct directory. -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MissingProjectException
+27
View File
@@ -0,0 +1,27 @@
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.069 s <<< FAILURE! -- in x.FailTest
[ERROR] x.FailTest.this_will_fail -- Time elapsed: 0.030 s <<< FAILURE!
org.opentest4j.AssertionFailedError: deliberate failure for rtk -q filter testing ==> expected: <1> but was: <2>
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:563)
at x.FailTest.this_will_fail(FailTest.java:9)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
[ERROR] Failures:
[ERROR] FailTest.this_will_fail:9 deliberate failure for rtk -q filter testing ==> expected: <1> but was: <2>
[ERROR] Tests run: 6, Failures: 1, Errors: 0, Skipped: 0
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.5:test (default-test) on project mvnq: There are test failures.
[ERROR]
[ERROR] See /tmp/mvnq/target/surefire-reports for the individual test results.
[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
+87
View File
@@ -0,0 +1,87 @@
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] multi-module-skeleton [pom]
[INFO] child-a [jar]
[INFO] child-b [jar]
[INFO]
[INFO] ---------------< com.example.rtk:multi-module-skeleton >----------------
[INFO] Building multi-module-skeleton 1.0.0-SNAPSHOT [1/3]
[INFO] from pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ multi-module-skeleton ---
[INFO] Installing /tmp/rtk-reactor/pom.xml to /home/vcyber/.m2/repository/com/example/rtk/multi-module-skeleton/1.0.0-SNAPSHOT/multi-module-skeleton-1.0.0-SNAPSHOT.pom
[INFO]
[INFO] ----------------------< com.example.rtk:child-a >-----------------------
[INFO] Building child-a 1.0.0-SNAPSHOT [2/3]
[INFO] from child-a/pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ child-a ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-a/src/main/resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ child-a ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ child-a ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-a/src/test/resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ child-a ---
[INFO] No sources to compile
[INFO]
[INFO] --- surefire:2.17:test (default-test) @ child-a ---
[INFO] No tests to run.
[INFO]
[INFO] --- jar:3.1.2:jar (default-jar) @ child-a ---
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ child-a ---
[INFO] Installing /tmp/rtk-reactor/child-a/pom.xml to /home/vcyber/.m2/repository/com/example/rtk/child-a/1.0.0-SNAPSHOT/child-a-1.0.0-SNAPSHOT.pom
[INFO] Installing /tmp/rtk-reactor/child-a/target/child-a-1.0.0-SNAPSHOT.jar to /home/vcyber/.m2/repository/com/example/rtk/child-a/1.0.0-SNAPSHOT/child-a-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] ----------------------< com.example.rtk:child-b >-----------------------
[INFO] Building child-b 1.0.0-SNAPSHOT [3/3]
[INFO] from child-b/pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ child-b ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-b/src/main/resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ child-b ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 1 source file with javac [debug target 21] to target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /tmp/rtk-reactor/child-b/src/main/java/Empty.java:[1,22] cannot find symbol
symbol: class BogusType
location: class Empty
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for multi-module-skeleton 1.0.0-SNAPSHOT:
[INFO]
[INFO] multi-module-skeleton .............................. SUCCESS [ 0.369 s]
[INFO] child-a ............................................ SUCCESS [ 1.657 s]
[INFO] child-b ............................................ FAILURE [ 0.940 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.193 s
[INFO] Finished at: 2026-05-25T12:11:41-03:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project child-b: Compilation failure
[ERROR] /tmp/rtk-reactor/child-b/src/main/java/Empty.java:[1,22] cannot find symbol
[ERROR] symbol: class BogusType
[ERROR] location: class Empty
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR] mvn <args> -rf :child-b
+79
View File
@@ -0,0 +1,79 @@
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] multi-module-skeleton [pom]
[INFO] child-a [jar]
[INFO] child-b [jar]
[INFO]
[INFO] ---------------< com.example.rtk:multi-module-skeleton >----------------
[INFO] Building multi-module-skeleton 1.0.0-SNAPSHOT [1/3]
[INFO] from pom.xml
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ multi-module-skeleton ---
[INFO] Installing /tmp/rtk-reactor/pom.xml to /home/vcyber/.m2/repository/com/example/rtk/multi-module-skeleton/1.0.0-SNAPSHOT/multi-module-skeleton-1.0.0-SNAPSHOT.pom
[INFO]
[INFO] ----------------------< com.example.rtk:child-a >-----------------------
[INFO] Building child-a 1.0.0-SNAPSHOT [2/3]
[INFO] from child-a/pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ child-a ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-a/src/main/resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ child-a ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ child-a ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-a/src/test/resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ child-a ---
[INFO] No sources to compile
[INFO]
[INFO] --- surefire:2.17:test (default-test) @ child-a ---
[INFO] No tests to run.
[INFO]
[INFO] --- jar:3.1.2:jar (default-jar) @ child-a ---
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ child-a ---
[INFO] Installing /tmp/rtk-reactor/child-a/pom.xml to /home/vcyber/.m2/repository/com/example/rtk/child-a/1.0.0-SNAPSHOT/child-a-1.0.0-SNAPSHOT.pom
[INFO] Installing /tmp/rtk-reactor/child-a/target/child-a-1.0.0-SNAPSHOT.jar to /home/vcyber/.m2/repository/com/example/rtk/child-a/1.0.0-SNAPSHOT/child-a-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] ----------------------< com.example.rtk:child-b >-----------------------
[INFO] Building child-b 1.0.0-SNAPSHOT [3/3]
[INFO] from child-b/pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ child-b ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-b/src/main/resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ child-b ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ child-b ---
[INFO] skip non existing resourceDirectory /tmp/rtk-reactor/child-b/src/test/resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ child-b ---
[INFO] No sources to compile
[INFO]
[INFO] --- surefire:2.17:test (default-test) @ child-b ---
[INFO] No tests to run.
[INFO]
[INFO] --- jar:3.1.2:jar (default-jar) @ child-b ---
[INFO]
[INFO] --- install:3.1.2:install (default-install) @ child-b ---
[INFO] Installing /tmp/rtk-reactor/child-b/pom.xml to /home/vcyber/.m2/repository/com/example/rtk/child-b/1.0.0-SNAPSHOT/child-b-1.0.0-SNAPSHOT.pom
[INFO] Installing /tmp/rtk-reactor/child-b/target/child-b-1.0.0-SNAPSHOT.jar to /home/vcyber/.m2/repository/com/example/rtk/child-b/1.0.0-SNAPSHOT/child-b-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for multi-module-skeleton 1.0.0-SNAPSHOT:
[INFO]
[INFO] multi-module-skeleton .............................. SUCCESS [ 0.353 s]
[INFO] child-a ............................................ SUCCESS [ 1.676 s]
[INFO] child-b ............................................ SUCCESS [ 0.069 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.284 s
[INFO] Finished at: 2026-05-25T12:11:31-03:00
[INFO] ------------------------------------------------------------------------
+29
View File
@@ -0,0 +1,29 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< commons-cli:commons-cli >-----------------------
[INFO] Building Apache Commons CLI 1.11.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- compiler:3.15.0:compile (default-compile) @ commons-cli ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 37 source files with javac [debug release 8] to target/classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] src/main/java/org/apache/commons/cli/CompileBreaker.java:[21,16] cannot find symbol
symbol: variable bar
location: class org.apache.commons.cli.CompileBreaker
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.214 s
[INFO] Finished at: 2026-05-21T15:02:31Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.15.0:compile (default-compile) on project commons-cli: Compilation failure
[ERROR] /home/runner/commons-cli/src/main/java/org/apache/commons/cli/CompileBreaker.java:[21,16] cannot find symbol
[ERROR] symbol: variable bar
[ERROR] location: class org.apache.commons.cli.CompileBreaker
[ERROR] -> [Help 1]
+46
View File
@@ -0,0 +1,46 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< commons-cli:commons-cli >-----------------------
[INFO] Building Apache Commons CLI 1.11.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- surefire:3.5.5:test (default-test) @ commons-cli ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.commons.cli.SolrCliTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s -- in org.apache.commons.cli.SolrCliTest
[INFO] Running org.apache.commons.cli.RtkInducedFailTest
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.033 s <<< FAILURE! -- in org.apache.commons.cli.RtkInducedFailTest
[ERROR] org.apache.commons.cli.RtkInducedFailTest.rtkInducedFailure -- Time elapsed: 0.025 s <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <expected> but was: <actual>
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1145)
at org.apache.commons.cli.RtkInducedFailTest.rtkInducedFailure(RtkInducedFailTest.java:25)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
[INFO] Running org.apache.commons.cli.PatternOptionBuilderTest
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.066 s -- in org.apache.commons.cli.PatternOptionBuilderTest
[INFO]
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR] RtkInducedFailTest.rtkInducedFailure:25 expected: <expected> but was: <actual>
[INFO]
[ERROR] Tests run: 978, Failures: 1, Errors: 0, Skipped: 61
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:05 min
[INFO] Finished at: 2026-05-21T14:57:09Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.5:test (default-test) on project commons-cli: There are test failures.
+70
View File
@@ -0,0 +1,70 @@
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------< com.example.rtk:multifail-skeleton >-----------------
[INFO] Building multifail-skeleton 1.0.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- surefire:3.5.5:test (default-test) @ multifail-skeleton ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.rtk.CalcTest
[ERROR] Tests run: 3, Failures: 1, Errors: 1, Skipped: 0, Time elapsed: 0.108 s <<< FAILURE! -- in com.example.rtk.CalcTest
[ERROR] com.example.rtk.CalcTest.failOne -- Time elapsed: 0.050 s <<< FAILURE!
org.opentest4j.AssertionFailedError: failOne: addition should equal five ==> expected: <5> but was: <4>
at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:563)
at com.example.rtk.CalcTest.failOne(CalcTest.java:12)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
[ERROR] com.example.rtk.CalcTest.failTwo -- Time elapsed: 0.006 s <<< ERROR!
java.lang.IllegalStateException: failTwo: induced error for fixture capture
at com.example.rtk.CalcTest.failTwo(CalcTest.java:17)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
[INFO] Running com.example.rtk.BoomTest
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.006 s <<< FAILURE! -- in com.example.rtk.BoomTest
[ERROR] com.example.rtk.BoomTest.boom -- Time elapsed: 0.004 s <<< ERROR!
java.lang.IllegalStateException: boom: errors-only class for fixture capture
at com.example.rtk.BoomTest.boom(BoomTest.java:8)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
[INFO]
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR] CalcTest.failOne:12 failOne: addition should equal five ==> expected: <5> but was: <4>
[ERROR] Errors:
[ERROR] BoomTest.boom:8 IllegalState boom: errors-only class for fixture capture
[ERROR] CalcTest.failTwo:17 IllegalState failTwo: induced error for fixture capture
[INFO]
[ERROR] Tests run: 4, Failures: 1, Errors: 2, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.171 s
[INFO] Finished at: 2026-06-06T10:53:18-03:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.5:test (default-test) on project multifail-skeleton: There are test failures.
[ERROR]
[ERROR] See /home/vcyber/projetos/rtk/tests/fixtures/multifail-skeleton/target/surefire-reports for the individual test results.
[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< commons-cli:commons-cli >-----------------------
[INFO] Building Apache Commons CLI 1.11.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- surefire:3.5.5:test (default-test) @ commons-cli ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.commons.cli.help.UtilTest
[INFO] Tests run: 34, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.023 s -- in org.apache.commons.cli.help.UtilTest
[INFO] Running org.apache.commons.cli.help.TextStyleTest
[INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.240 s -- in org.apache.commons.cli.help.TextStyleTest
[INFO] Running org.apache.commons.cli.DefaultParserTest
[WARNING] Tests run: 87, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 1.023 s -- in org.apache.commons.cli.DefaultParserTest
[INFO] Running org.apache.commons.cli.ConverterTests
[INFO] Tests run: 14, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.204 s -- in org.apache.commons.cli.ConverterTests
[INFO] Running org.apache.commons.cli.AlreadySelectedExceptionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 s -- in org.apache.commons.cli.AlreadySelectedExceptionTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 977, Failures: 0, Errors: 0, Skipped: 61
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:14 min
[INFO] Finished at: 2026-05-21T14:54:30Z
[INFO] ------------------------------------------------------------------------
+450
View File
@@ -0,0 +1,450 @@
{
"apiVersion": "v1",
"kind": "List",
"metadata": {
"resourceVersion": ""
},
"items": [
{
"metadata": {
"name": "alertmanager-main-0",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "alertmanager",
"restartCount": 0
},
{
"name": "config-reloader",
"restartCount": 0
},
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-metric",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prom-label-proxy",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "alertmanager-main-1",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "alertmanager",
"restartCount": 0
},
{
"name": "config-reloader",
"restartCount": 0
},
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-metric",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prom-label-proxy",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "cluster-monitoring-operator-7fb9fd4c5d-wm2k6",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "cluster-monitoring-operator",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "kube-state-metrics-75586fdddb-5zw6b",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy-main",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-self",
"restartCount": 0
},
{
"name": "kube-state-metrics",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "metrics-server-fdfb77995-jmqnm",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "metrics-server",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "metrics-server-fdfb77995-w4r8p",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "metrics-server",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "monitoring-plugin-85b45d978c-27rfm",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "monitoring-plugin",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "monitoring-plugin-85b45d978c-64g98",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "monitoring-plugin",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "node-exporter-5fw9f",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "node-exporter",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "node-exporter-gbnrd",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "node-exporter",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "openshift-state-metrics-856bc7fff5-p8s92",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy-main",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-self",
"restartCount": 0
},
{
"name": "openshift-state-metrics",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "prometheus-k8s-0",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "config-reloader",
"restartCount": 0
},
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-thanos",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prometheus",
"restartCount": 0
},
{
"name": "thanos-sidecar",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "prometheus-k8s-1",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "config-reloader",
"restartCount": 0
},
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-thanos",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prometheus",
"restartCount": 0
},
{
"name": "thanos-sidecar",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "prometheus-operator-5978c4f47f-9zwdq",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "prometheus-operator",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "prometheus-operator-admission-webhook-6485c485f4-8cfhf",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "prometheus-operator-admission-webhook",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "prometheus-operator-admission-webhook-6485c485f4-b655d",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "prometheus-operator-admission-webhook",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "telemeter-client-5d75f79f95-hr22b",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "reload",
"restartCount": 0
},
{
"name": "telemeter-client",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "thanos-querier-5fcf655bd-4h2m2",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-metrics",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-rules",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prom-label-proxy",
"restartCount": 0
},
{
"name": "thanos-query",
"restartCount": 0
}
]
}
},
{
"metadata": {
"name": "thanos-querier-5fcf655bd-wd28r",
"namespace": "openshift-monitoring"
},
"status": {
"phase": "Running",
"containerStatuses": [
{
"name": "kube-rbac-proxy",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-metrics",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-rules",
"restartCount": 0
},
{
"name": "kube-rbac-proxy-web",
"restartCount": 0
},
{
"name": "prom-label-proxy",
"restartCount": 0
},
{
"name": "thanos-query",
"restartCount": 0
}
]
}
}
]
}
+380
View File
@@ -0,0 +1,380 @@
{
"totals": {
"errors": 47,
"file_errors": 47
},
"files": {
"/var/www/project/app/Models/User.php": {
"errors": 6,
"messages": [
{
"message": "Property User::$id (int) does not accept null.",
"line": 15,
"ignorable": false,
"identifier": "property.nonObject",
"tip": "Use ?int if null is a valid value."
},
{
"message": "Property User::$email (string) does not accept null.",
"line": 18,
"ignorable": false,
"identifier": "property.nonObject",
"tip": null
},
{
"message": "Method User::find() has no return type specified.",
"line": 45,
"ignorable": true,
"identifier": "missingType.return",
"tip": "Add @return type annotation or declare the return type."
},
{
"message": "Call to an undefined method Illuminate\\Database\\Eloquent\\Model::unknownMethod().",
"line": 67,
"ignorable": false,
"identifier": "method.notFound",
"tip": null
},
{
"message": "Parameter #1 $id of method User::find() expects int, string given.",
"line": 89,
"ignorable": false,
"identifier": "argument.type",
"tip": null
},
{
"message": "Dead catch - Exception is never thrown in the try block.",
"line": 120,
"ignorable": true,
"identifier": "deadCode.catch",
"tip": null
}
]
},
"/var/www/project/app/Http/Controllers/UserController.php": {
"errors": 5,
"messages": [
{
"message": "Parameter $request of method UserController::store() has no type specified.",
"line": 34,
"ignorable": false,
"identifier": "missingType.parameter",
"tip": null
},
{
"message": "Variable $user might not be defined.",
"line": 56,
"ignorable": false,
"identifier": "variable.undefined",
"tip": null
},
{
"message": "Binary operation '+' between string and int results in an error.",
"line": 78,
"ignorable": true,
"identifier": "binaryOp.invalid",
"tip": null
},
{
"message": "Unreachable statement - code above always terminates.",
"line": 95,
"ignorable": false,
"identifier": "deadCode.unreachable",
"tip": null
},
{
"message": "Result of method UserRepository::create() (void) is used.",
"line": 112,
"ignorable": false,
"identifier": "return.void",
"tip": null
}
]
},
"/var/www/project/app/Services/AuthService.php": {
"errors": 4,
"messages": [
{
"message": "Method AuthService::login() should return User but returns User|null.",
"line": 23,
"ignorable": false,
"identifier": "return.type",
"tip": null
},
{
"message": "Comparison operation '>' between int<0, max> and 0 is always true.",
"line": 45,
"ignorable": true,
"identifier": "comparison.alwaysTrue",
"tip": null
},
{
"message": "Parameter #2 $password of function password_verify expects string, int given.",
"line": 67,
"ignorable": false,
"identifier": "argument.type",
"tip": null
},
{
"message": "Property AuthService::$logger is never read, only written.",
"line": 89,
"ignorable": true,
"identifier": "deadCode.property",
"tip": null
}
]
},
"/var/www/project/app/Repositories/UserRepository.php": {
"errors": 3,
"messages": [
{
"message": "Access to an undefined property User::$unknownField.",
"line": 28,
"ignorable": false,
"identifier": "property.notFound",
"tip": null
},
{
"message": "Method UserRepository::all() should return Collection<User> but returns Collection.",
"line": 52,
"ignorable": false,
"identifier": "return.type",
"tip": "Specify the collection type parameter."
},
{
"message": "Instanceof between User and stdClass will always evaluate to false.",
"line": 78,
"ignorable": false,
"identifier": "instanceof.alwaysFalse",
"tip": null
}
]
},
"/var/www/project/app/Http/Middleware/Authenticate.php": {
"errors": 2,
"messages": [
{
"message": "Method Authenticate::handle() has parameter $next with no type specified.",
"line": 19,
"ignorable": false,
"identifier": "missingType.parameter",
"tip": null
},
{
"message": "Negated boolean expression is always false.",
"line": 34,
"ignorable": true,
"identifier": "booleanNot.alwaysFalse",
"tip": null
}
]
},
"/var/www/project/app/Console/Commands/SyncUsers.php": {
"errors": 3,
"messages": [
{
"message": "Method SyncUsers::handle() has no return type specified.",
"line": 22,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Variable $users might not be defined.",
"line": 45,
"ignorable": false,
"identifier": "variable.undefined",
"tip": null
},
{
"message": "Call to function array_map() with Closure and array|null will result in an error.",
"line": 67,
"ignorable": false,
"identifier": "argument.type",
"tip": null
}
]
},
"/var/www/project/app/Events/UserRegistered.php": {
"errors": 2,
"messages": [
{
"message": "Property UserRegistered::$user has no type specified.",
"line": 12,
"ignorable": false,
"identifier": "missingType.property",
"tip": null
},
{
"message": "Call to an undefined method Illuminate\\Events\\Dispatcher::dispatch().",
"line": 30,
"ignorable": false,
"identifier": "method.notFound",
"tip": null
}
]
},
"/var/www/project/app/Listeners/SendWelcomeEmail.php": {
"errors": 2,
"messages": [
{
"message": "Parameter $event of method SendWelcomeEmail::handle() has no type specified.",
"line": 18,
"ignorable": false,
"identifier": "missingType.parameter",
"tip": null
},
{
"message": "Variable $mailer might not be defined.",
"line": 35,
"ignorable": false,
"identifier": "variable.undefined",
"tip": null
}
]
},
"/var/www/project/app/Policies/UserPolicy.php": {
"errors": 3,
"messages": [
{
"message": "Method UserPolicy::view() has no return type specified.",
"line": 25,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Method UserPolicy::create() has no return type specified.",
"line": 35,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Method UserPolicy::update() has no return type specified.",
"line": 45,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
}
]
},
"/var/www/project/app/Observers/UserObserver.php": {
"errors": 2,
"messages": [
{
"message": "Method UserObserver::created() has parameter $user with no type specified.",
"line": 14,
"ignorable": false,
"identifier": "missingType.parameter",
"tip": null
},
{
"message": "Method UserObserver::deleted() has parameter $user with no type specified.",
"line": 24,
"ignorable": false,
"identifier": "missingType.parameter",
"tip": null
}
]
},
"/var/www/project/app/Providers/AuthServiceProvider.php": {
"errors": 2,
"messages": [
{
"message": "Method AuthServiceProvider::boot() has no return type specified.",
"line": 30,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Variable $gate might not be defined.",
"line": 45,
"ignorable": false,
"identifier": "variable.undefined",
"tip": null
}
]
},
"/var/www/project/app/Jobs/ProcessPayment.php": {
"errors": 4,
"messages": [
{
"message": "Method ProcessPayment::handle() has no return type specified.",
"line": 35,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Property ProcessPayment::$amount has no type specified.",
"line": 18,
"ignorable": false,
"identifier": "missingType.property",
"tip": null
},
{
"message": "Variable $payment might not be defined.",
"line": 58,
"ignorable": false,
"identifier": "variable.undefined",
"tip": null
},
{
"message": "Call to an undefined method PaymentGateway::charge().",
"line": 72,
"ignorable": false,
"identifier": "method.notFound",
"tip": null
}
]
},
"/var/www/project/app/Mail/WelcomeEmail.php": {
"errors": 2,
"messages": [
{
"message": "Method WelcomeEmail::build() has no return type specified.",
"line": 28,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Property WelcomeEmail::$user has no type specified.",
"line": 15,
"ignorable": false,
"identifier": "missingType.property",
"tip": null
}
]
},
"/var/www/project/app/Http/Requests/StoreUserRequest.php": {
"errors": 3,
"messages": [
{
"message": "Method StoreUserRequest::rules() has no return type specified.",
"line": 18,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Method StoreUserRequest::authorize() has no return type specified.",
"line": 12,
"ignorable": false,
"identifier": "missingType.return",
"tip": null
},
{
"message": "Strict comparison using === between string and int will always evaluate to false.",
"line": 32,
"ignorable": false,
"identifier": "comparison.strict",
"tip": null
}
]
}
},
"errors": []
}
+28
View File
@@ -0,0 +1,28 @@
Downloading cpython-3.13.5-macos-aarch64-none (download) (14.9MiB)
Downloading cpython-3.13.5-macos-aarch64-none (download)
Using CPython 3.13.5
Creating virtual environment at: .venv
Downloading pygments (1.2MiB)
Downloading pygments
Installed 5 packages in 7ms
============================= test session starts ==============================
platform darwin -- Python 3.13.5, pytest-8.4.1, pluggy-1.6.0
rootdir: /private/tmp/rtk-uv-fixture
configfile: pyproject.toml
collected 2 items
tests/test_users.py .F [100%]
=================================== FAILURES ===================================
______________________ test_normalize_user_rejects_empty _______________________
def test_normalize_user_rejects_empty():
> assert normalize_user(" ") == "anonymous"
E AssertionError: assert '' == 'anonymous'
E
E - anonymous
tests/test_users.py:10: AssertionError
=========================== short test summary info ============================
FAILED tests/test_users.py::test_normalize_user_rejects_empty - AssertionErro...
========================= 1 failed, 1 passed in 0.01s ==========================
+171
View File
@@ -0,0 +1,171 @@
#![cfg(unix)]
//! Regressions from the #2333 grep overhaul: -A/-B/-C must keep context lines,
//! -c/-o must not leak ripgrep's NUL separator.
use std::process::Command;
fn rtk() -> Command {
Command::new(env!("CARGO_BIN_EXE_rtk"))
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[test]
fn after_context_lines_are_shown() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("ctx.txt");
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\nafter2\nafter3\n").unwrap();
let out = rtk()
.args(["grep", "-A2", "MATCHME", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("after1"),
"missing after-context line 1:\n{stdout}"
);
assert!(
stdout.contains("after2"),
"missing after-context line 2:\n{stdout}"
);
assert!(!stdout.contains("after3"), "leaked beyond -A2:\n{stdout}");
}
#[test]
fn before_context_lines_are_shown() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("ctx.txt");
std::fs::write(&f, "before1\nbefore2\nMATCHME\nafter1\n").unwrap();
let out = rtk()
.args(["grep", "-B1", "MATCHME", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("before2"),
"missing before-context line:\n{stdout}"
);
assert!(!stdout.contains("before1"), "leaked beyond -B1:\n{stdout}");
}
#[test]
fn count_output_has_no_nul_separator() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nul.txt");
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
let out = rtk()
.args(["grep", "-c", "TODO", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains('\u{0}'),
"NUL leaked into -c output: {stdout:?}"
);
assert!(
stdout.contains('2'),
"count missing from -c output: {stdout:?}"
);
}
#[test]
fn group_separator_between_non_adjacent_matches() {
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("sep.txt");
std::fs::write(
&f,
"alpha match here\nfiller 1\nfiller 2\nfiller 3\nfiller 4\nfiller 5\nbeta match here\n",
)
.unwrap();
let out = rtk()
.args(["grep", "-A1", "match", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("--"),
"missing group separator between non-adjacent match blocks:\n{stdout}"
);
assert!(
stdout.contains("alpha match here"),
"first match must be present:\n{stdout}"
);
assert!(
stdout.contains("beta match here"),
"second match must be present:\n{stdout}"
);
}
#[test]
fn no_separator_without_context_flag() {
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nosep.txt");
std::fs::write(
&f,
"match line 1\nfiller\nfiller\nfiller\nmatch line 2\nfiller\nfiller\nmatch line 3\n",
)
.unwrap();
let out = rtk()
.args(["grep", "-n", "match", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("--"),
"plain grep without -A/-B/-C must not insert -- separators:\n{stdout}"
);
assert!(
stdout.contains("match line 1"),
"first match must be present:\n{stdout}"
);
assert!(
stdout.contains("match line 3"),
"third match must be present:\n{stdout}"
);
}
#[test]
fn only_matching_output_has_no_nul_separator() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("nul.txt");
std::fs::write(&f, "TODO one\nnope\nTODO two\n").unwrap();
let out = rtk()
.args(["grep", "-o", "TODO", f.to_str().unwrap()])
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains('\u{0}'),
"NUL leaked into -o output: {stdout:?}"
);
assert!(
stdout.contains("TODO"),
"match missing from -o output: {stdout:?}"
);
assert!(
!stdout.contains("1:TODO"),
"line-number prefix leaked into -o output: {stdout:?}"
);
}
+175
View File
@@ -0,0 +1,175 @@
#![cfg(unix)]
//! For un-capped searches `rtk grep X` must be byte-identical to `grep -n X`:
//! line number always, filename only when grep itself prints one. Covers content
//! and regex edge cases (colons, digits, shell metachars, unicode, colon-in-path)
//! that must never be misparsed.
use std::io::Write;
use std::process::{Command, Stdio};
fn rtk_grep(args: &[&str]) -> (String, Option<i32>) {
let mut a = vec!["grep"];
a.extend_from_slice(args);
let out = Command::new(env!("CARGO_BIN_EXE_rtk"))
.args(&a)
.output()
.expect("rtk");
(
String::from_utf8_lossy(&out.stdout).into_owned(),
out.status.code(),
)
}
fn grep_n(args: &[&str]) -> (String, Option<i32>) {
let mut a = vec!["-n"];
a.extend_from_slice(args);
let out = Command::new("grep").args(&a).output().expect("grep");
(
String::from_utf8_lossy(&out.stdout).into_owned(),
out.status.code(),
)
}
fn assert_eq_grep_n(args: &[&str]) {
let (rtk, rc) = rtk_grep(args);
let (grep, gc) = grep_n(args);
assert_eq!(rtk, grep, "stdout mismatch for {args:?}");
assert_eq!(rc, gc, "exit code mismatch for {args:?}");
}
fn write(dir: &std::path::Path, name: &str, body: &str) -> String {
let p = dir.join(name);
std::fs::write(&p, body).expect("write");
p.to_str().unwrap().to_string()
}
#[test]
fn single_multi_recursive_and_h_match_grep_n() {
let d = tempfile::tempdir().unwrap();
let f1 = write(d.path(), "f1.txt", "apple\nzebra apple\nbanana\n");
let f2 = write(d.path(), "f2.txt", "apricot\n");
assert_eq_grep_n(&["apple", &f1]); // single: position only
assert_eq_grep_n(&["a", &f1, &f2]); // multi: file + position
assert_eq_grep_n(&["-H", "apple", &f1]); // -H forces filename on a single file
assert_eq_grep_n(&["-r", "a", d.path().to_str().unwrap()]); // recursive
}
#[test]
fn no_match_matches_grep_n() {
let d = tempfile::tempdir().unwrap();
let f = write(d.path(), "f.txt", "hello\n");
assert_eq_grep_n(&["zzz_no_match_xyz", &f]); // empty stdout, exit 1
}
#[test]
fn nasty_content_is_not_misparsed() {
let d = tempfile::tempdir().unwrap();
let f = write(
d.path(),
"n.txt",
"12:34 looks like a line number\na::b::c ClassRegistry::init('x')\nport :8080: here\n$(rm -rf /) `whoami` && echo\n中文 テスト مرحبا\n",
);
assert_eq_grep_n(&[":", &f]);
assert_eq_grep_n(&["ClassRegistry", &f]);
assert_eq_grep_n(&["中文", &f]);
assert_eq_grep_n(&["whoami", &f]);
}
#[test]
fn regex_metacharacters_match_grep_n() {
let d = tempfile::tempdir().unwrap();
let f = write(d.path(), "r.txt", "a.b\naxb\nfoo.bar\n[x]\n");
assert_eq_grep_n(&["a.b", &f]); // . is any-char
assert_eq_grep_n(&["-F", "a.b", &f]); // fixed-string
assert_eq_grep_n(&["^foo", &f]); // anchor
assert_eq_grep_n(&["-E", "ax?b", &f]); // ERE
}
#[test]
fn context_flags_match_grep_n() {
let d = tempfile::tempdir().unwrap();
let f = write(d.path(), "c.txt", "x\nMATCH\ny\nz\n");
assert_eq_grep_n(&["-A1", "MATCH", &f]);
assert_eq_grep_n(&["-B1", "MATCH", &f]);
assert_eq_grep_n(&["-C1", "MATCH", &f]);
}
#[test]
fn context_group_separator_matches_grep_n() {
let d = tempfile::tempdir().unwrap();
let f = write(
d.path(),
"sep.txt",
"match A\nfill1\nfill2\nfill3\nmatch B\n",
);
assert_eq_grep_n(&["-A1", "match", &f]);
}
// #1436: a single-file `-n` search with `::` content and a BRE pattern with
// literal `()` must equal grep exactly — no broadened matches, and no synthetic
// `[file]` bucket from splitting on `::`.
#[test]
fn issue_1436_colons_and_literal_parens() {
let d = tempfile::tempdir().unwrap();
let f = write(
d.path(),
"shell.php",
"use Util\\ClassRegistry;\nclass Foo {\n public function run() {\n $this->m = ClassRegistry::init('Collections.QueueProcess');\n try {\n deleteRow($id);\n $this->delete();\n } catch (\\Exception $e) {\n } finally {}\n }\n private function delete() {}\n}\n",
);
assert_eq_grep_n(&[
"private function delete\\|try\\|catch\\|finally\\|delete()",
&f,
]);
assert_eq_grep_n(&["init", &f]); // ClassRegistry::init must stay one intact line
}
// #1436 (comments): a leading `^` anchor must stay scoped to the given file
// (tenequm), and a line ending in `:` (Python `if x:`) must keep full content
// (BTCAlchemist).
#[test]
fn issue_1436_anchor_scope_and_trailing_colon() {
let d = tempfile::tempdir().unwrap();
let f = write(d.path(), "code.rs", "pub fn a\nprivate b\npub fn c\n");
let py = write(
d.path(),
"t.py",
"x = 1\nif crypto >= MAX_CRYPTO:\nclass Foo:\n",
);
assert_eq_grep_n(&["^pub", &f]); // anchored pattern must not escape the path
assert_eq_grep_n(&["MAX_CRYPTO", &py]); // trailing-colon line kept intact
assert_eq_grep_n(&["class", &py]);
}
#[test]
fn colon_in_filename_matches_grep_n() {
let d = tempfile::tempdir().unwrap();
let f1 = write(d.path(), "weird:name.txt", "hit\n");
let f2 = write(d.path(), "other.txt", "hit\n");
assert_eq_grep_n(&["hit", &f1, &f2]); // colon in a path must not fool the parser
}
#[test]
fn case_insensitive_and_invert_match_grep_n() {
let d = tempfile::tempdir().unwrap();
let f = write(d.path(), "i.txt", "Apple\nbanana\nAPPLE\n");
assert_eq_grep_n(&["-i", "apple", &f]);
assert_eq_grep_n(&["-v", "apple", &f]);
}
#[test]
fn piped_stdin_matches_grep_n() {
let input = "apple\nzebra\napple pie\n";
let feed = |cmd: &mut Command| {
let mut c = cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn");
c.stdin.take().unwrap().write_all(input.as_bytes()).unwrap();
String::from_utf8_lossy(&c.wait_with_output().unwrap().stdout).into_owned()
};
let rtk = feed(Command::new(env!("CARGO_BIN_EXE_rtk")).args(["grep", "apple"]));
let grep = feed(Command::new("grep").args(["-n", "apple"]));
assert_eq!(rtk, grep, "piped stdin must equal grep -n");
}
+269
View File
@@ -0,0 +1,269 @@
//! End-to-end proof of the never-worse guard (src/core/guard.rs).
use std::io::Write;
use std::process::{Command, Stdio};
fn rtk_stdin(args: &[&str], input: &str) -> String {
let mut child = Command::new(env!("CARGO_BIN_EXE_rtk"))
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
.expect("spawn rtk");
child
.stdin
.take()
.expect("stdin")
.write_all(input.as_bytes())
.expect("write stdin");
let out = child.wait_with_output().expect("wait rtk");
String::from_utf8_lossy(&out.stdout).into_owned()
}
#[test]
fn guard_shows_raw_when_filter_would_bloat_tiny_input() {
let input = "{\"a\":1,\"b\":2,\"c\":3,\"d\":4}";
let out = rtk_stdin(&["json", "-"], input);
assert_eq!(
out.trim(),
input,
"guard should emit the raw minified JSON, not a larger pretty-printed form"
);
assert!(
out.trim().len() <= input.len(),
"never-worse violated: {} chars emitted for a {}-char raw input",
out.trim().len(),
input.len()
);
}
#[test]
fn guard_does_not_block_real_compression() {
let mut input = String::from("{");
for i in 0..60 {
input.push_str(&format!("\"key_{i}\":\"value_{i}\","));
}
input.push_str("\"last\":1}");
let out = rtk_stdin(&["json", "-"], &input);
assert!(
out.len() < input.len(),
"filter should compress large input (guard must not over-trigger): {} vs {}",
out.len(),
input.len()
);
}
fn rtk_output_in_dir(dir: &std::path::Path, args: &[&str]) -> (String, String, Option<i32>) {
let out = Command::new(env!("CARGO_BIN_EXE_rtk"))
.args(args)
.current_dir(dir)
.output()
.expect("spawn rtk");
(
String::from_utf8_lossy(&out.stdout).into_owned(),
String::from_utf8_lossy(&out.stderr).into_owned(),
out.status.code(),
)
}
fn rtk_in_dir(dir: &std::path::Path, args: &[&str]) -> (String, Option<i32>) {
let (stdout, _, code) = rtk_output_in_dir(dir, args);
(stdout, code)
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn init_git_repo() -> tempfile::TempDir {
let dir = tempfile::tempdir().expect("tempdir");
for args in [
&["init", "-q", "-b", "main"][..],
&["config", "user.email", "t@t.t"][..],
&["config", "user.name", "t"][..],
&["commit", "-q", "--allow-empty", "-m", "init"][..],
] {
let ok = Command::new("git")
.args(args)
.current_dir(dir.path())
.output()
.map(|o| o.status.success())
.unwrap_or(false);
assert!(ok, "git setup failed: {args:?}");
}
dir
}
fn git_in_dir(dir: &std::path::Path, args: &[&str]) {
let out = Command::new("git")
.args(args)
.current_dir(dir)
.output()
.expect("spawn git");
assert!(
out.status.success(),
"git command failed: {args:?}\nstdout: {}\nstderr: {}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
}
fn read_text_normalized(path: &std::path::Path) -> String {
std::fs::read_to_string(path).unwrap().replace("\r\n", "\n")
}
#[test]
fn grep_no_match_emits_empty_not_a_message() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.txt"), "hello world\n").expect("write");
// Faithful grep needs -r to descend a directory; we no longer force recursion
// by routing through rg (the engine-faithful contract).
let (out, code) = rtk_in_dir(dir.path(), &["grep", "-r", "zzz_no_match_xyz", "."]);
assert!(
out.trim().is_empty(),
"no-match grep must emit empty, not a '0 matches' line: {out:?}"
);
assert_eq!(code, Some(1), "grep no-match must preserve exit 1");
}
#[test]
fn find_no_results_emits_empty() {
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.txt"), "x").expect("write");
let (out, _) = rtk_in_dir(dir.path(), &["find", ".", "-name", "zzz_no_match_xyz"]);
assert!(
out.trim().is_empty(),
"no-result find must emit empty, not a '0 for' line: {out:?}"
);
}
#[test]
fn git_stash_list_no_stashes_emits_empty() {
let dir = init_git_repo();
let (out, code) = rtk_in_dir(dir.path(), &["git", "stash", "list"]);
assert!(
out.trim().is_empty(),
"no-stashes must emit empty, not 'No stashes': {out:?}"
);
assert_eq!(code, Some(0));
}
#[test]
fn git_stash_show_no_stash_emits_empty_and_propagates_failure() {
// Regression: previously printed "Empty stash" and returned Ok(0), masking
// the underlying `git stash show` failure.
let dir = init_git_repo();
let (out, code) = rtk_in_dir(dir.path(), &["git", "stash", "show"]);
assert!(
out.trim().is_empty(),
"must emit empty, not 'Empty stash': {out:?}"
);
assert_ne!(
code,
Some(0),
"a real git stash show failure must not be masked as exit 0"
);
}
#[test]
fn git_checkout_branch_switch_emits_compact_ok() {
let dir = init_git_repo();
git_in_dir(dir.path(), &["checkout", "-q", "-b", "feature/test"]);
let (out, code) = rtk_in_dir(dir.path(), &["git", "checkout", "main"]);
assert_eq!(code, Some(0));
assert_eq!(out.trim(), "ok main");
}
#[test]
fn git_checkout_new_branch_emits_compact_ok() {
let dir = init_git_repo();
let (out, code) = rtk_in_dir(dir.path(), &["git", "checkout", "-b", "feature/test"]);
assert_eq!(code, Some(0));
assert_eq!(out.trim(), "ok feature/test (new)");
}
#[test]
fn git_checkout_reset_branch_does_not_claim_new_branch() {
let dir = init_git_repo();
let (out, code) = rtk_in_dir(dir.path(), &["git", "checkout", "-B", "feature/test"]);
assert_eq!(code, Some(0));
assert_eq!(out.trim(), "ok feature/test");
}
#[test]
fn git_checkout_file_restore_emits_restored_count() {
let dir = init_git_repo();
std::fs::write(dir.path().join("a.txt"), "original\n").expect("write a");
std::fs::write(dir.path().join("b.txt"), "original\n").expect("write b");
git_in_dir(dir.path(), &["add", "a.txt", "b.txt"]);
git_in_dir(dir.path(), &["commit", "-q", "-m", "add files"]);
std::fs::write(dir.path().join("a.txt"), "changed\n").expect("write a");
std::fs::write(dir.path().join("b.txt"), "changed\n").expect("write b");
let (out, code) = rtk_in_dir(
dir.path(),
&["git", "checkout", "HEAD", "--", "a.txt", "b.txt"],
);
assert_eq!(code, Some(0));
assert!(
out.trim().is_empty() || out.trim() == "ok 2 files restored",
"guarded output may stay empty when native git emits no success text: {out:?}"
);
assert_eq!(
read_text_normalized(&dir.path().join("a.txt")),
"original\n"
);
assert_eq!(
read_text_normalized(&dir.path().join("b.txt")),
"original\n"
);
}
#[test]
fn git_checkout_dirty_tree_error_keeps_file_list() {
let dir = init_git_repo();
std::fs::write(dir.path().join("a.txt"), "main\n").expect("write a");
git_in_dir(dir.path(), &["add", "a.txt"]);
git_in_dir(dir.path(), &["commit", "-q", "-m", "add a"]);
git_in_dir(dir.path(), &["checkout", "-q", "-b", "feature/test"]);
std::fs::write(dir.path().join("a.txt"), "feature\n").expect("write feature");
git_in_dir(dir.path(), &["commit", "-am", "feature change"]);
git_in_dir(dir.path(), &["checkout", "-q", "main"]);
std::fs::write(dir.path().join("a.txt"), "dirty\n").expect("write dirty");
let (stdout, stderr, code) =
rtk_output_in_dir(dir.path(), &["git", "checkout", "feature/test"]);
let combined = format!("{stdout}{stderr}");
assert_ne!(code, Some(0));
assert!(
combined.contains("error:"),
"dirty checkout failure should keep error header: {combined:?}"
);
assert!(
combined.contains("a.txt"),
"dirty checkout failure should keep conflicting filename: {combined:?}"
);
assert!(
combined.contains("Aborting"),
"dirty checkout failure should keep abort line: {combined:?}"
);
}
+378
View File
@@ -0,0 +1,378 @@
#![cfg(unix)]
//! Integration tests for the shared grep/rg compression filter: the GROUP path
//! with context flags (-A/-B/-C) and the safety-net passthrough for flags (some
//! grep-only like -I, some rg-only like --heading/-p) that break the NUL reparse.
use std::process::Command;
fn rtk() -> Command {
Command::new(env!("CARGO_BIN_EXE_rtk"))
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn write_temp(content: &str) -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("test.txt");
std::fs::write(&path, content).expect("write");
(dir, path)
}
// --- context compression (the gain win) ---
#[test]
fn single_file_context_shown_without_header() {
if !rg_available() {
return;
}
let long = "x".repeat(120);
let content =
format!("before {long}\nMATCH {long}\nafter1 {long}\nafter2 {long}\nend {long}\n");
let (_dir, path) = write_temp(&content);
let out = rtk()
.args(["grep", "-A2", "MATCH", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("matches in"),
"single-file search must not add a grouped header:\n{stdout}"
);
assert!(
stdout.contains("after1") && stdout.contains("after2"),
"after-context lines must be shown:\n{stdout}"
);
}
#[test]
fn after_context_uses_dash_separator_for_context_lines() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("MATCH\nafter1\n");
let out = rtk()
.args(["grep", "-nA1", "MATCH", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
let has_context_dash = stdout.lines().any(|l| l.contains("-after1"));
assert!(
has_context_dash,
"context lines must use dash separator:\n{stdout}"
);
}
#[test]
fn capped_single_file_shows_header() {
if !rg_available() {
return;
}
let filler: String = (0..40).map(|i| format!("w{i} ")).collect();
let content: String = (0..60).map(|i| format!("foo {i} {filler}\n")).collect();
let (_dir, path) = write_temp(&content);
let out = rtk()
.args(["grep", "foo", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("matches in"),
"header must show once capping compresses:\n{stdout}"
);
}
#[test]
fn true_no_match_exits_1() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["grep", "zzzz_no_match_xyz", path.to_str().unwrap()])
.output()
.expect("rtk grep");
assert_eq!(
out.status.code(),
Some(1),
"true no-match must exit 1, not 0"
);
}
// --- safety net: flags that break the NUL-based reparse ---
#[test]
fn no_line_number_flag_produces_output_not_zero_matches() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["rg", "-N", "hello", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("0 matches"),
"-N output must not be reported as '0 matches':\n{stdout}"
);
assert!(
stdout.contains("hello"),
"-N output must contain the matching line:\n{stdout}"
);
}
#[test]
fn no_filename_flag_produces_output_not_zero_matches() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["grep", "-I", "hello", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("0 matches"),
"-I output must not be reported as '0 matches':\n{stdout}"
);
assert!(
stdout.contains("hello"),
"-I output must contain the matching line:\n{stdout}"
);
}
#[test]
fn heading_flag_produces_output_not_zero_matches() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["rg", "--heading", "hello", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("0 matches"),
"--heading output must not be reported as '0 matches':\n{stdout}"
);
assert!(
stdout.contains("hello"),
"--heading output must contain the matching line:\n{stdout}"
);
}
#[test]
fn pretty_flag_produces_output_not_zero_matches() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["rg", "-p", "hello", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("0 matches"),
"-p output must not be reported as '0 matches':\n{stdout}"
);
assert!(
stdout.contains("hello"),
"-p output must contain the matching line:\n{stdout}"
);
}
// --- shape flags: passthrough, no NUL leak ---
#[test]
fn column_flag_output_has_no_nul() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("hello world\n");
let out = rtk()
.args(["rg", "--column", "hello", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains('\u{0}'),
"--column output must not contain NUL:\n{stdout:?}"
);
assert!(
stdout.contains("hello"),
"--column output must contain the match:\n{stdout}"
);
}
// --- token savings (the compression gain) ---
fn count_tokens(s: &str) -> usize {
s.split_whitespace().count()
}
// Covers #545: grep savings are measured against the real grep output.
#[test]
fn bulky_grep_yields_token_savings() {
if !rg_available() {
return;
}
let filler: String = (0..50).map(|i| format!("word{i} ")).collect();
let mut content = String::new();
for i in 0..60 {
content.push_str(&format!("MATCH line {i} {filler}\n"));
}
let (_dir, path) = write_temp(&content);
let raw = Command::new("rg")
.args(["-nH", "MATCH", path.to_str().unwrap()])
.output()
.expect("rg");
let raw_tokens = count_tokens(&String::from_utf8_lossy(&raw.stdout));
let out = rtk()
.args(["grep", "MATCH", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let rtk_tokens = count_tokens(&String::from_utf8_lossy(&out.stdout));
let savings = 100.0 - (rtk_tokens as f64 / raw_tokens as f64 * 100.0);
assert!(
savings >= 50.0,
"expected >=50% token savings, got {savings:.1}% (raw={raw_tokens}, rtk={rtk_tokens})"
);
}
// --- grep-only syntax falls back to system grep (#2543) ---
#[test]
fn grep_only_flags_fall_back_to_system_grep() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.java"), "DRAFT in java\n").expect("write");
std::fs::write(dir.path().join("b.txt"), "DRAFT in text\n").expect("write");
let path = dir.path().to_str().unwrap();
let out = rtk()
.args(["grep", "-rn", "DRAFT", "--include=*.java", path])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("a.java"),
"--include=*.java should match the java file:\n{stdout}"
);
assert!(
!stdout.contains("b.txt"),
"--include=*.java should exclude the txt file:\n{stdout}"
);
assert!(
!stdout.contains("grep failed") && !stdout.contains("unrecognized"),
"rg-incompatible flag must fall back to grep, not error:\n{stdout}"
);
}
// --- never-worse guard: small greps must not cost more than plain grep ---
#[test]
fn small_grep_not_worse_than_plain() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("foo\n");
let out = rtk()
.args(["grep", "foo", path.to_str().unwrap()])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.trim() == "1:foo",
"single-file grep must equal `grep -n` (position, no filename):\n{stdout}"
);
assert!(
!stdout.contains("matches in"),
"header that costs more than raw must be dropped:\n{stdout}"
);
}
// --- #2543: bundled files-with-matches cluster (-rln / -ln) lists files, not "0 matches" ---
#[test]
fn bundled_files_with_matches_cluster_lists_files() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.txt"), "TODO alpha\n").expect("write");
std::fs::write(dir.path().join("b.txt"), "TODO beta\n").expect("write");
std::fs::write(dir.path().join("c.txt"), "nothing here\n").expect("write");
let path = dir.path().to_str().unwrap();
let out = rtk()
.args(["grep", "-rln", "TODO", path])
.output()
.expect("rtk grep");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("0 matches"),
"-rln must list files, not report a false '0 matches' (#2543):\n{stdout}"
);
assert!(
stdout.contains("a.txt") && stdout.contains("b.txt"),
"-rln must list every matching file:\n{stdout}"
);
assert!(
!stdout.contains("c.txt"),
"-rln must not list non-matching files:\n{stdout}"
);
}
// A match inside a binary file is noise (grep prints only a "binary file matches"
// notice); skip it by default, but `-a` lets the agent opt back into the content.
#[test]
fn binary_match_is_skipped_unless_text_requested() {
let dir = tempfile::tempdir().expect("tempdir");
let p = dir.path().join("blob.bin");
std::fs::write(&p, b"SECRET\x00\x01binary\xff\xfe").expect("write");
let path = p.to_str().unwrap();
let out = rtk().args(["grep", "SECRET", path]).output().expect("rtk");
assert_eq!(
out.status.code(),
Some(1),
"binary match must be skipped as noise by default"
);
assert!(out.stdout.is_empty(), "no binary content by default");
let out = rtk()
.args(["grep", "-a", "SECRET", path])
.output()
.expect("rtk -a");
assert_eq!(out.status.code(), Some(0), "-a must surface the match");
assert!(
String::from_utf8_lossy(&out.stdout).contains("SECRET"),
"-a must show the binary content"
);
}
+123
View File
@@ -0,0 +1,123 @@
#![cfg(unix)]
//! Error/exit faithfulness: rtk surfaces the engine's own stderr and propagates
//! its exit code, adding nothing (no synthetic "search failed" line). An engine
//! error (exit >=2) must never look like a silent no-match (#2465 / #1436).
use std::process::Command;
fn rtk(args: &[&str]) -> std::process::Output {
Command::new(env!("CARGO_BIN_EXE_rtk"))
.args(args)
.output()
.expect("rtk")
}
fn grep_exit(args: &[&str]) -> Option<i32> {
Command::new("grep")
.args(args)
.output()
.expect("grep")
.status
.code()
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn write_temp(content: &str) -> (tempfile::TempDir, String) {
let d = tempfile::tempdir().expect("tempdir");
let p = d.path().join("ok.txt");
std::fs::write(&p, content).expect("write");
let s = p.to_str().unwrap().to_string();
(d, s)
}
#[test]
fn file_not_found_propagates_exit_and_surfaces_error() {
let d = tempfile::tempdir().unwrap();
let missing = d.path().join("nope.txt");
let m = missing.to_str().unwrap();
let out = rtk(&["grep", "fn", m]);
assert_eq!(
out.status.code(),
grep_exit(&["fn", m]),
"exit must match grep"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("No such file"),
"the engine's error must be surfaced:\n{stderr}"
);
assert!(
!stderr.contains("search failed"),
"no synthetic line:\n{stderr}"
);
}
#[test]
fn bad_regex_surfaces_error_never_silent_no_match() {
let (_d, f) = write_temp("fn alpha\n");
let out = rtk(&["grep", "[", &f]);
assert_eq!(
out.status.code(),
Some(2),
"a bad regex must propagate exit 2, not a silent exit-1 no-match"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(!stderr.is_empty(), "the engine error must surface");
assert!(
!stderr.contains("search failed"),
"nothing added:\n{stderr}"
);
}
#[test]
fn partial_match_with_error_surfaces_both() {
let (_d, f) = write_temp("fn alpha\nfn beta\n");
let d2 = tempfile::tempdir().unwrap();
let missing = d2.path().join("nope.txt");
let out = rtk(&["grep", "fn", &f, missing.to_str().unwrap()]);
assert_eq!(out.status.code(), Some(2), "the error exit must propagate");
assert!(
String::from_utf8_lossy(&out.stderr).contains("No such file"),
"the file error must surface alongside the matches"
);
assert!(
!out.stdout.is_empty(),
"the matches from the readable file must still be shown"
);
}
#[test]
fn no_match_stays_exit_1_and_empty() {
let (_d, f) = write_temp("fn alpha\n");
let out = rtk(&["grep", "zzzz_no_match", &f]);
assert_eq!(out.status.code(), Some(1), "no-match is exit 1");
assert!(
out.stdout.is_empty() && out.stderr.is_empty(),
"no-match emits nothing"
);
}
#[test]
fn rg_bad_regex_surfaces_error_exit_2() {
if !rg_available() {
return;
}
let (_d, f) = write_temp("GET /objects/{path}\n");
let out = rtk(&["rg", "a|{path}", &f]);
assert_eq!(
out.status.code(),
Some(2),
"rg's regex parse error must propagate exit 2"
);
assert!(
!String::from_utf8_lossy(&out.stderr).contains("search failed"),
"nothing added"
);
}
+348
View File
@@ -0,0 +1,348 @@
#![cfg(unix)]
//! Engine-faithfulness contract: `rtk grep` runs grep and `rtk rg` runs rg, each
//! with its own regex dialect and ignore semantics. rtk filters output noise only;
//! it never substitutes one engine for the other (the bug this PR removes).
use std::io::Write;
use std::process::{Command, Stdio};
fn rtk() -> Command {
Command::new(env!("CARGO_BIN_EXE_rtk"))
}
/// Run rtk with `input` fed on stdin; returns (stdout, exit_code).
fn rtk_stdin(input: &str, args: &[&str]) -> (String, Option<i32>) {
let mut child = rtk()
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn rtk");
child
.stdin
.take()
.expect("stdin")
.write_all(input.as_bytes())
.expect("write stdin");
let out = child.wait_with_output().expect("wait");
(
String::from_utf8_lossy(&out.stdout).into_owned(),
out.status.code(),
)
}
fn rg_available() -> bool {
Command::new("rg")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn write_temp(content: &str) -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("test.txt");
std::fs::write(&path, content).expect("write");
(dir, path)
}
fn count_tokens(s: &str) -> usize {
s.split_whitespace().count()
}
// --- regex dialect: the single most important regression guard ---
// Covers #2253 (matches silently rewritten when grep was routed through rg),
// #2301 (rg routed to BSD grep, wrong dialect) and #545 (savings can't be faked
// against a foreign engine's output).
#[test]
fn grep_and_rg_use_their_own_regex_dialect() {
if !rg_available() {
return;
}
// "alpha" holds "al" and "gamma" holds "ga", but neither line ever contains
// the literal string "al|ga".
let (_dir, path) = write_temp("alpha\ngamma\n");
let p = path.to_str().unwrap();
// grep BRE: a bare `|` is a literal pipe, so "al|ga" matches nothing -> exit 1.
let g = rtk().args(["grep", "al|ga", p]).output().expect("rtk grep");
assert_eq!(
g.status.code(),
Some(1),
"grep must treat | as a literal pipe (0 matches), proving no rg substitution:\n{}",
String::from_utf8_lossy(&g.stdout)
);
// rg: `|` is alternation, so "al|ga" matches both lines -> exit 0.
let r = rtk().args(["rg", "al|ga", p]).output().expect("rtk rg");
assert_eq!(
r.status.code(),
Some(0),
"rg must treat | as alternation (matches), proving it runs rg, not grep"
);
let r_out = String::from_utf8_lossy(&r.stdout);
assert!(
r_out.contains("alpha") && r_out.contains("gamma"),
"rg alternation must match both lines:\n{r_out}"
);
}
// --- ignore semantics: rg stays lean, grep -r descends (the node_modules flood fix) ---
// Covers #2606 (rg must keep .gitignore/binary skipping) and #2064 (forced
// --no-ignore-vcs made rg read node_modules and dump minified files).
#[test]
fn rg_honors_ignore_files_grep_does_not() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
// `.ignore` is rg-native and applies without a surrounding git repo.
std::fs::write(dir.path().join(".ignore"), "skip.txt\n").expect("write");
std::fs::write(dir.path().join("skip.txt"), "NEEDLE here\n").expect("write");
std::fs::write(dir.path().join("keep.txt"), "NEEDLE here\n").expect("write");
let path = dir.path().to_str().unwrap();
// rg respects the ignore file: skip.txt must not appear (no forced --no-ignore-vcs).
let r = rtk().args(["rg", "NEEDLE", path]).output().expect("rtk rg");
let r_out = String::from_utf8_lossy(&r.stdout);
assert!(
r_out.contains("keep.txt"),
"rg must match the non-ignored file:\n{r_out}"
);
assert!(
!r_out.contains("skip.txt"),
"rg must honor .ignore and skip skip.txt (no forced --no-ignore-vcs flood):\n{r_out}"
);
// grep knows nothing of ignore files: -r descends and matches both.
let g = rtk()
.args(["grep", "-rn", "NEEDLE", path])
.output()
.expect("rtk grep");
let g_out = String::from_utf8_lossy(&g.stdout);
assert!(
g_out.contains("keep.txt") && g_out.contains("skip.txt"),
"grep -r must descend into every file, ignoring .ignore:\n{g_out}"
);
}
// --- token savings: rg path compresses as much as the grep path ---
// Covers #545: rg savings are measured against the real rg output, not a
// substituted engine's inflated result.
#[test]
fn bulky_rg_yields_token_savings() {
if !rg_available() {
return;
}
let filler: String = (0..50).map(|i| format!("word{i} ")).collect();
let mut content = String::new();
for i in 0..60 {
content.push_str(&format!("MATCH line {i} {filler}\n"));
}
let (_dir, path) = write_temp(&content);
let raw = Command::new("rg")
.args(["-nH", "MATCH", path.to_str().unwrap()])
.output()
.expect("rg");
let raw_tokens = count_tokens(&String::from_utf8_lossy(&raw.stdout));
let out = rtk()
.args(["rg", "MATCH", path.to_str().unwrap()])
.output()
.expect("rtk rg");
let rtk_tokens = count_tokens(&String::from_utf8_lossy(&out.stdout));
let savings = 100.0 - (rtk_tokens as f64 / raw_tokens as f64 * 100.0);
assert!(
savings >= 50.0,
"expected >=50% rg token savings, got {savings:.1}% (raw={raw_tokens}, rtk={rtk_tokens})"
);
}
// --- engine-specific flags reach their own engine ---
// `-E` selects grep's extended regex; the flag must reach grep, never be swallowed
// as ripgrep's `--encoding` (#2253) or routed to the wrong engine (#2167).
#[test]
fn grep_dash_e_selects_extended_regex() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("alpha\ngamma\n");
let p = path.to_str().unwrap();
// Plain BRE: bare `|` is literal -> no match, exit 1.
let bre = rtk().args(["grep", "al|ga", p]).output().expect("rtk grep");
assert_eq!(bre.status.code(), Some(1), "BRE | must stay literal");
// -E (ERE): `|` is alternation -> both lines match, exit 0.
let ere = rtk()
.args(["grep", "-E", "al|ga", p])
.output()
.expect("rtk grep -E");
assert_eq!(
ere.status.code(),
Some(0),
"grep -E must enable ERE alternation, proving -E reaches grep and is not read as rg --encoding"
);
let out = String::from_utf8_lossy(&ere.stdout);
assert!(
out.contains("alpha") && out.contains("gamma"),
"grep -E alternation must match both lines:\n{out}"
);
}
// rg's `-g/--glob` filters the file set; it must reach rg with correct ordering,
// not be mangled by a grep that has no such flag (#1824, #2167).
#[test]
fn rg_glob_flag_filters_the_file_set() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("a.rs"), "NEEDLE in rust\n").expect("write");
std::fs::write(dir.path().join("b.txt"), "NEEDLE in text\n").expect("write");
let path = dir.path().to_str().unwrap();
let out = rtk()
.args(["rg", "-g", "*.rs", "NEEDLE", path])
.output()
.expect("rtk rg -g");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("a.rs"),
"rg -g '*.rs' must match the rust file:\n{stdout}"
);
assert!(
!stdout.contains("b.txt"),
"rg -g '*.rs' must exclude files outside the glob:\n{stdout}"
);
}
// --- piped stdin: read the pipe, never inject a path ---
#[test]
fn grep_reads_piped_stdin() {
let (out, code) = rtk_stdin("alpha\nmatch me here\nomega\n", &["grep", "match"]);
assert_eq!(
code,
Some(0),
"stdin match must exit 0, got {code:?}:\n{out}"
);
assert!(
out.contains("match me here"),
"grep must read stdin and emit the match:\n{out}"
);
assert!(
!out.contains("Is a directory"),
"RTK must not inject '.' and break a stdin pipe:\n{out}"
);
}
#[test]
fn rg_reads_piped_stdin_without_flooding_cwd() {
if !rg_available() {
return;
}
// `fn` matches thousands of repo lines; reading stdin must yield only the pipe.
let (out, code) = rtk_stdin("fn stdin_only_line\n", &["rg", "fn"]);
assert_eq!(
code,
Some(0),
"stdin match must exit 0, got {code:?}:\n{out}"
);
assert!(
out.contains("stdin_only_line"),
"rg must read stdin and emit the match:\n{out}"
);
assert!(
!out.contains(".rs:") && !out.contains("src/"),
"rg must read stdin, not flood the working tree:\n{out}"
);
}
#[test]
fn grep_counts_piped_stdin_via_passthrough() {
// `-c` routes to the passthrough path; it must still read the pipe, not null stdin.
let (out, code) = rtk_stdin("hit\nmiss\nhit\n", &["grep", "-c", "hit"]);
assert_eq!(
code,
Some(0),
"stdin count must exit 0, got {code:?}:\n{out}"
);
assert_eq!(
out.trim(),
"2",
"grep -c must count matches from stdin:\n{out}"
);
}
// --- pattern-less commands run instead of being blocked ---
#[test]
fn rg_type_list_passes_through() {
if !rg_available() {
return;
}
let out = rtk().args(["rg", "--type-list"]).output().expect("rtk rg");
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
assert_eq!(out.status.code(), Some(0), "--type-list must exit 0");
assert!(
stdout.lines().count() > 10,
"--type-list must produce the real type list:\n{stdout}"
);
assert!(
!stdout.contains("pattern required") && !stderr.contains("pattern required"),
"valid pattern-less command must not be blocked:\n{stdout}{stderr}"
);
assert!(
!stderr.contains("rtk grep:"),
"an rg command must never be labelled 'rtk grep:':\n{stderr}"
);
}
#[test]
fn rg_files_lists_the_given_dir() {
if !rg_available() {
return;
}
let dir = tempfile::tempdir().expect("tempdir");
std::fs::write(dir.path().join("only_here.txt"), "x\n").expect("write");
let path = dir.path().to_str().unwrap();
let out = rtk()
.args(["rg", "--files", path])
.output()
.expect("rtk rg");
let stdout = String::from_utf8_lossy(&out.stdout);
assert_eq!(out.status.code(), Some(0), "--files must exit 0");
assert!(
stdout.contains("only_here.txt"),
"--files must list the given dir, not the cwd:\n{stdout}"
);
}
// #1436 (jhagberg): a pattern rg rejects must surface rg's error (exit 2), never
// be swallowed into a silent "0 matches" (exit 1) that reads as a clean no-match.
#[test]
fn rg_surfaces_regex_error_not_silent_zero() {
if !rg_available() {
return;
}
let (_dir, path) = write_temp("GET /objects/{path}\nfilePath: x\n");
let out = rtk()
.args(["rg", "objects|{path}", path.to_str().unwrap()])
.output()
.expect("rtk rg");
assert_eq!(
out.status.code(),
Some(2),
"an rg regex parse error must surface as exit 2, not a silent 0/1"
);
}