raider_reporter 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +12 -9
- data/lib/raider_reporter/collector.rb +11 -1
- data/lib/raider_reporter/configuration.rb +12 -1
- data/lib/raider_reporter/content_types.rb +10 -2
- data/lib/raider_reporter/models.rb +6 -1
- data/lib/raider_reporter/payload.rb +4 -1
- data/lib/raider_reporter/quarantine.rb +1 -1
- data/lib/raider_reporter/version.rb +1 -1
- data/lib/raider_reporter/writers/html.rb +7 -0
- data/lib/raider_reporter/writers/uploader.rb +11 -11
- data/lib/raider_reporter.rb +5 -7
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5caf918bfe636da31412f0d9befd2122cdb81c1c207ec594b5e60f11a9aa930
|
|
4
|
+
data.tar.gz: 96b844a743d018f47b54cbb80c3552ab2a76e2be6b247095099efa2457f07a3a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c030e57458d0a854dd9d52fef8086301b52d62cfa181e689c5a30f88cc6b6feb108510c767183d3abb3cac7ed0deeb1037e0ed7b9b4a80357e95c1ca805bf06
|
|
7
|
+
data.tar.gz: '0382b224926f9a6595f66355102afd825ced75a0e107bdf65c37c8cb7af8d5a93cfef66ef8a5f7952bb63351c9c9f16c226afd28ca5c46112daf29240da29343'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0 — 2026-11-01
|
|
4
|
+
|
|
5
|
+
- Raider traces: the content type `application/vnd.raider.trace+zip` (`type: :trace`) gets its own size cap, `config.max_trace_bytes` (default 25 MB). The 5 MB `max_attachment_bytes` and the 50 MB `max_video_bytes` still apply to everything else. A trace over its cap is skipped with a warning
|
|
6
|
+
- `RaiderReporter.attach(name, path, type:, attempt:)` takes an optional `attempt`, written to the attachment's entry in `run.json` and in the upload when given
|
|
7
|
+
- Uploads send screenshots and other files first, then traces, then videos, in the room left in the budget. A trace that does not fit is skipped and later, smaller ones are still tried
|
|
8
|
+
- The local HTML report shows a trace as a download link ("Trace (open in Raider Desktop)", with the attempt when known)
|
|
9
|
+
- `.zip` files resolve to `application/zip`
|
|
10
|
+
- No breaking changes
|
|
11
|
+
|
|
3
12
|
## 0.11.0 — 2026-10-29
|
|
4
13
|
|
|
5
14
|
- Live events: when `RAIDER_EVENTS_DIR` is set, the RSpec formatter and the Cucumber formatter/plugin append NDJSON lines (`plan`, `started`, `finished`, `done`, each with `"v":1`) to `<dir>/worker-<n>.ndjson`, where `n` is the shard index (1 without a shard). Cucumber writes `started` and `finished` once per attempt under `--retry`. The Raider desktop app tails them during a run. The files are created owner-only (0600) and a symlinked file is refused. With the variable unset, nothing changes. A write error switches the stream off after one warning and never fails the suite
|
data/README.md
CHANGED
|
@@ -101,7 +101,7 @@ Cucumber's `attach` is captured only when given the path of an existing file. In
|
|
|
101
101
|
|
|
102
102
|
`parallel_tests` works with no configuration for Cucumber too: each process writes `run-<n>.json`. See [Parallel runs](#parallel-runs).
|
|
103
103
|
|
|
104
|
-
## Attaching screenshots and
|
|
104
|
+
## Attaching screenshots, videos and traces
|
|
105
105
|
|
|
106
106
|
Call `RaiderReporter.attach` while an example is running, for example from an `after` hook:
|
|
107
107
|
|
|
@@ -120,11 +120,13 @@ end
|
|
|
120
120
|
|
|
121
121
|
`attach` copies the file right away, so the attachment keeps the bytes the file had at that moment even if the same path is overwritten by a later example. Using a path per example (as above) still makes the files on disk easier to find.
|
|
122
122
|
|
|
123
|
-
`attach` returns `true` on success. It returns `false` and prints a `[raider_reporter]` warning, never raising, if it is called outside a running example (for instance when the formatter is not loaded), if the file does not exist, or if the file is larger than its size cap: 5 MB (`max_attachment_bytes`),
|
|
123
|
+
`attach` returns `true` on success. It returns `false` and prints a `[raider_reporter]` warning, never raising, if it is called outside a running example (for instance when the formatter is not loaded), if the file does not exist, or if the file is larger than its size cap: 5 MB (`max_attachment_bytes`), 50 MB for videos (`video/*`, `max_video_bytes`), or 25 MB for traces (`max_trace_bytes`).
|
|
124
124
|
|
|
125
125
|
Videos (`.webm`, `.mp4`) are attached the same way, for example `RaiderReporter.attach("video", path, type: :webm)`. In the local HTML report they play inline, with a download link.
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
Raider traces (`trace.zip`, content type `application/vnd.raider.trace+zip`, written by ruby_raider projects for failing tests) are attached with `RaiderReporter.attach("trace", path, type: :trace, attempt: 2)`. `attempt` is optional on any attachment. When given, it is written to the attachment's entry in `run.json` and in the upload, so a viewer can tell the traces of different retries apart. In the local HTML report a trace is a download link; open it in Raider Desktop. The format is described in the [trace viewer design](https://github.com/RaiderHQ/raider_planning/blob/main/docs/superpowers/specs/2026-11-01-trace-viewer-design.md).
|
|
128
|
+
|
|
129
|
+
When a run is uploaded, screenshots and other attachments are sent first, then traces, then videos, in the room left in the upload budget. A trace or video that does not fit is skipped and later, smaller ones are still tried. The local `raider-results/attachments/` folder keeps every attachment that passed its size cap.
|
|
128
130
|
|
|
129
131
|
## Live events
|
|
130
132
|
|
|
@@ -142,13 +144,14 @@ end
|
|
|
142
144
|
| Option | Default | Description |
|
|
143
145
|
| --- | --- | --- |
|
|
144
146
|
| `token` | `ENV["RAIDER_TOKEN"]` | API token used for uploads. |
|
|
145
|
-
| `endpoint` | `ENV["RAIDER_ENDPOINT"]` | Base URL of
|
|
147
|
+
| `endpoint` | `ENV["RAIDER_ENDPOINT"]` | Base URL of your Raider Hideout server (what its setup page or bin/rails hideout:setup prints as RAIDER_ENDPOINT). |
|
|
146
148
|
| `output_dir` | `"raider-results"` | Directory for local output. |
|
|
147
149
|
| `clean_output_dir` | `true` | Remove the previous `run.json` and `attachments/` directory (`run-<n>.json` and `attachments-<n>/` when sharded) inside `output_dir` before writing. Nothing else in `output_dir` is touched, except a raider_reporter-written `index.html`. |
|
|
148
150
|
| `html_report` | `true` | Write `index.html`, the local HTML report, into `output_dir` after each run. See [Local report](#local-report). |
|
|
149
151
|
| `quarantine` | `true` when `ENV["RAIDER_QUARANTINE"]` is `1` or `true` | Fetch the quarantine list at run start and report matching failures as `quarantined`. Needs `token` and `endpoint`. See [Quarantine](#quarantine). |
|
|
150
152
|
| `max_attachment_bytes` | `5 MB` | Largest non-video attachment `attach` accepts. |
|
|
151
153
|
| `max_video_bytes` | `50 MB` | Largest `video/*` attachment `attach` accepts. |
|
|
154
|
+
| `max_trace_bytes` | `25 MB` | Largest Raider trace (`application/vnd.raider.trace+zip`) `attach` accepts. |
|
|
152
155
|
| `exec_summary_path` | `ENV["RAIDER_EXEC_SUMMARY"]` | File where the run writes its failure counts (framework failures, quarantined, errors outside tests) for `raider-reporter exec` to read when it decides the exit code. `exec` sets it for the command it runs; leave it unset otherwise. Nothing is written when it is unset. See [Quarantine](#quarantine). |
|
|
153
156
|
| `environment` | `{}` | Free-form key/values stored with the run (browser, suite name, and so on). Values are JSON values: strings, booleans or numbers. |
|
|
154
157
|
|
|
@@ -180,7 +183,7 @@ Set `config.html_report = false` to turn it off. An `index.html` that raider_rep
|
|
|
180
183
|
If both a token and an endpoint are set, the run (plus attachments) is uploaded when the suite finishes:
|
|
181
184
|
|
|
182
185
|
```
|
|
183
|
-
RAIDER_TOKEN=... RAIDER_ENDPOINT=https://
|
|
186
|
+
RAIDER_TOKEN=... RAIDER_ENDPOINT=https://hideout.example.com bundle exec rspec
|
|
184
187
|
```
|
|
185
188
|
|
|
186
189
|
On success the formatter prints `Raider report: <url>` to stderr (so tools that parse RSpec's stdout, such as `--format json`, stay clean), using the build URL when the server returns one and the run URL otherwise (or `Raider report uploaded (run <id>)` if the response has no URL). If either value is missing, nothing is uploaded and the local files are still written.
|
|
@@ -189,7 +192,7 @@ An upload carries at most 45 MB of attachments and at most 1000 attachment files
|
|
|
189
192
|
|
|
190
193
|
Reporting is fail-open: no error inside the gem, network failure, timeout or non-2xx response will raise or change RSpec's exit status. Each connect/write/read step of the upload times out after 30 seconds. Problems are printed as warnings prefixed with `[raider_reporter]`. An endpoint without `http://` or `https://` (for example `localhost:3000`) produces a warning and nothing is uploaded; a trailing slash is fine.
|
|
191
194
|
|
|
192
|
-
|
|
195
|
+
Raider Hideout is self-hosted: run your own server and set `RAIDER_ENDPOINT` to its URL. There is no hosted service.
|
|
193
196
|
|
|
194
197
|
## Quarantine
|
|
195
198
|
|
|
@@ -234,10 +237,10 @@ On pull requests, `raider-reporter comment` posts one comment with the build's r
|
|
|
234
237
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
235
238
|
```
|
|
236
239
|
|
|
237
|
-
- The comment is rendered by the Raider server (`GET /api/v1/builds/summary`); the gem only fetches the Markdown and posts it. The same comment is updated on every run instead of adding a new one.
|
|
240
|
+
- The comment is rendered by the Raider Hideout server (`GET /api/v1/builds/summary`); the gem only fetches the Markdown and posts it. The same comment is updated on every run instead of adding a new one.
|
|
238
241
|
- Pull requests from forks are skipped: GitHub gives their workflows a read-only token, so the command prints a notice and exits 0.
|
|
239
242
|
- The command never fails your CI: it always exits 0 and prints notices to stderr, prefixed `[raider_reporter]`. It needs `GITHUB_TOKEN`, `GITHUB_REPOSITORY`, `GITHUB_EVENT_PATH`, `RAIDER_TOKEN` and `RAIDER_ENDPOINT`; if one is missing, or the event is not a pull request, it does nothing.
|
|
240
|
-
- It requires a Raider server that supports the summary endpoint and the `git.pr` field. Upgrade the server before using it.
|
|
243
|
+
- It requires a Raider Hideout server that supports the summary endpoint and the `git.pr` field. Upgrade the server before using it.
|
|
241
244
|
|
|
242
245
|
## Quality gate
|
|
243
246
|
|
|
@@ -317,7 +320,7 @@ jobs:
|
|
|
317
320
|
- **Artifact names:** pipeline artifacts cannot be overwritten within a run, so the name includes the job position and attempt. A fixed name would fail when you re-run failed jobs or use `strategy: parallel`.
|
|
318
321
|
- **Azure Repos only:** the PR comment works for repositories hosted in Azure Repos. A GitHub-hosted repository built by Azure Pipelines still uploads results, but `comment` prints `skipped: not an Azure Repos pull request`.
|
|
319
322
|
- **Parallel jobs:** `strategy: parallel: N` shards are detected automatically and grouped into one build, like GitHub matrix jobs.
|
|
320
|
-
- The command never fails your pipeline: it always exits 0 and prints notices to stderr, prefixed `[raider_reporter]`. It needs `SYSTEM_ACCESSTOKEN`, `RAIDER_TOKEN` and `RAIDER_ENDPOINT`, and requires a Raider server that supports the summary endpoint.
|
|
323
|
+
- The command never fails your pipeline: it always exits 0 and prints notices to stderr, prefixed `[raider_reporter]`. It needs `SYSTEM_ACCESSTOKEN`, `RAIDER_TOKEN` and `RAIDER_ENDPOINT`, and requires a Raider Hideout server that supports the summary endpoint.
|
|
321
324
|
|
|
322
325
|
## Parallel runs
|
|
323
326
|
|
|
@@ -4,15 +4,18 @@ require "securerandom"
|
|
|
4
4
|
require "tmpdir"
|
|
5
5
|
require "fileutils"
|
|
6
6
|
require "set"
|
|
7
|
+
require_relative "content_types"
|
|
7
8
|
|
|
8
9
|
module RaiderReporter
|
|
9
10
|
# Accumulates one suite run. Framework formatters call it; writers read the Run it returns.
|
|
10
11
|
class Collector
|
|
11
12
|
def initialize(max_attachment_bytes:, warn:, max_video_bytes: Configuration::DEFAULT_MAX_VIDEO_BYTES,
|
|
13
|
+
max_trace_bytes: Configuration::DEFAULT_MAX_TRACE_BYTES,
|
|
12
14
|
clock: -> { Time.now.utc }, uuid: -> { SecureRandom.uuid },
|
|
13
15
|
quarantine: -> { Quarantine::EMPTY })
|
|
14
16
|
@max_attachment_bytes = max_attachment_bytes
|
|
15
17
|
@max_video_bytes = max_video_bytes
|
|
18
|
+
@max_trace_bytes = max_trace_bytes
|
|
16
19
|
@warn = warn
|
|
17
20
|
@clock = clock
|
|
18
21
|
@uuid = uuid
|
|
@@ -120,12 +123,19 @@ module RaiderReporter
|
|
|
120
123
|
@warn.call("attachment #{path} no longer exists; skipped")
|
|
121
124
|
return false
|
|
122
125
|
end
|
|
123
|
-
limit = attachment.content_type
|
|
126
|
+
limit = limit_for(attachment.content_type)
|
|
124
127
|
if File.size(path) > limit
|
|
125
128
|
@warn.call("attachment #{path} is larger than #{limit} bytes; skipped")
|
|
126
129
|
return false
|
|
127
130
|
end
|
|
128
131
|
true
|
|
129
132
|
end
|
|
133
|
+
|
|
134
|
+
def limit_for(content_type)
|
|
135
|
+
return @max_trace_bytes if ContentTypes.trace?(content_type)
|
|
136
|
+
return @max_video_bytes if ContentTypes.video?(content_type)
|
|
137
|
+
|
|
138
|
+
@max_attachment_bytes
|
|
139
|
+
end
|
|
130
140
|
end
|
|
131
141
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "raider_reporter/azure_env"
|
|
4
|
+
require "raider_reporter/content_types"
|
|
4
5
|
|
|
5
6
|
module RaiderReporter
|
|
6
7
|
class Configuration
|
|
7
8
|
TRUTHY = %w[1 true].freeze
|
|
8
9
|
DEFAULT_MAX_VIDEO_BYTES = 50 * 1024 * 1024
|
|
10
|
+
DEFAULT_MAX_TRACE_BYTES = 25 * 1024 * 1024
|
|
9
11
|
|
|
10
12
|
attr_accessor :token, :endpoint, :output_dir, :clean_output_dir, :html_report, :environment, :max_attachment_bytes,
|
|
11
|
-
:max_video_bytes, :quarantine, :exec_summary_path
|
|
13
|
+
:max_video_bytes, :max_trace_bytes, :quarantine, :exec_summary_path
|
|
12
14
|
|
|
13
15
|
def initialize(env = ENV)
|
|
14
16
|
@token = AzureEnv.value(env, "RAIDER_TOKEN")
|
|
@@ -19,10 +21,19 @@ module RaiderReporter
|
|
|
19
21
|
@environment = {}
|
|
20
22
|
@max_attachment_bytes = 5 * 1024 * 1024
|
|
21
23
|
@max_video_bytes = DEFAULT_MAX_VIDEO_BYTES
|
|
24
|
+
@max_trace_bytes = DEFAULT_MAX_TRACE_BYTES
|
|
22
25
|
@quarantine = TRUTHY.include?(AzureEnv.value(env, "RAIDER_QUARANTINE").to_s.downcase)
|
|
23
26
|
@exec_summary_path = AzureEnv.value(env, "RAIDER_EXEC_SUMMARY")
|
|
24
27
|
end
|
|
25
28
|
|
|
29
|
+
# The size cap for one attachment of this content type.
|
|
30
|
+
def attachment_limit(content_type)
|
|
31
|
+
return max_trace_bytes if ContentTypes.trace?(content_type)
|
|
32
|
+
return max_video_bytes if ContentTypes.video?(content_type)
|
|
33
|
+
|
|
34
|
+
max_attachment_bytes
|
|
35
|
+
end
|
|
36
|
+
|
|
26
37
|
def upload?
|
|
27
38
|
present?(token) && present?(endpoint)
|
|
28
39
|
end
|
|
@@ -2,18 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module RaiderReporter
|
|
4
4
|
module ContentTypes
|
|
5
|
+
TRACE = "application/vnd.raider.trace+zip"
|
|
6
|
+
|
|
5
7
|
BY_EXTENSION = {
|
|
6
8
|
".png" => "image/png", ".jpg" => "image/jpeg", ".jpeg" => "image/jpeg", ".gif" => "image/gif",
|
|
7
9
|
".webp" => "image/webp", ".mp4" => "video/mp4", ".webm" => "video/webm", ".mov" => "video/quicktime",
|
|
8
|
-
".txt" => "text/plain", ".log" => "text/plain", ".html" => "text/html", ".json" => "application/json"
|
|
10
|
+
".txt" => "text/plain", ".log" => "text/plain", ".html" => "text/html", ".json" => "application/json",
|
|
11
|
+
".zip" => "application/zip"
|
|
9
12
|
}.freeze
|
|
10
13
|
BY_SYMBOL = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", mp4: "video/mp4",
|
|
11
|
-
webm: "video/webm", text: "text/plain", html: "text/html", json: "application/json"
|
|
14
|
+
webm: "video/webm", text: "text/plain", html: "text/html", json: "application/json",
|
|
15
|
+
trace: TRACE }.freeze
|
|
12
16
|
|
|
13
17
|
MIME = %r{\A[\w.+-]+/[\w.+-]+\z}
|
|
14
18
|
|
|
15
19
|
module_function
|
|
16
20
|
|
|
21
|
+
def trace?(content_type) = content_type.to_s == TRACE
|
|
22
|
+
|
|
23
|
+
def video?(content_type) = content_type.to_s.start_with?("video/")
|
|
24
|
+
|
|
17
25
|
def resolve(type, path)
|
|
18
26
|
if type.is_a?(String)
|
|
19
27
|
return type if type.match?(MIME)
|
|
@@ -9,7 +9,12 @@ module RaiderReporter
|
|
|
9
9
|
super
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
# attempt: which try of the test produced it, when the caller knows (traces); nil otherwise.
|
|
13
|
+
Attachment = Data.define(:name, :source_path, :content_type, :attempt) do
|
|
14
|
+
def initialize(name:, source_path:, content_type:, attempt: nil)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
13
18
|
TestResult = Data.define(
|
|
14
19
|
:identifier, :full_name, :file_path, :line_number, :status,
|
|
15
20
|
:duration_ms, :attempts, :tags, :failure, :attachments
|
|
@@ -88,7 +88,10 @@ module RaiderReporter
|
|
|
88
88
|
|
|
89
89
|
def attachment_hash(entry, mode)
|
|
90
90
|
ref = mode == :local ? { "path" => entry[:path] } : { "part" => entry[:part] }
|
|
91
|
-
|
|
91
|
+
attachment = entry[:attachment]
|
|
92
|
+
hash = { "name" => attachment.name, "content_type" => attachment.content_type }.merge(ref)
|
|
93
|
+
hash["attempt"] = attachment.attempt if attachment.attempt
|
|
94
|
+
hash
|
|
92
95
|
end
|
|
93
96
|
end
|
|
94
97
|
end
|
|
@@ -11,7 +11,7 @@ module RaiderReporter
|
|
|
11
11
|
NUL = "\u0000"
|
|
12
12
|
NUL_REPLACEMENT = "\u{FFFD}"
|
|
13
13
|
|
|
14
|
-
# The server's test identity:
|
|
14
|
+
# The server's test identity: Raider Hideout's TestCase.key_for over the payload's
|
|
15
15
|
# file_path and full_name, after ingest replaced NUL in each of them.
|
|
16
16
|
def self.test_key(file_path, full_name)
|
|
17
17
|
parts = [file_path, full_name].map { |part| part.to_s.tr(NUL, NUL_REPLACEMENT) }
|
|
@@ -4,6 +4,7 @@ require "erb"
|
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require "json"
|
|
6
6
|
require "time"
|
|
7
|
+
require_relative "../content_types"
|
|
7
8
|
|
|
8
9
|
module RaiderReporter
|
|
9
10
|
module Writers
|
|
@@ -229,6 +230,8 @@ module RaiderReporter
|
|
|
229
230
|
%(<a href="#{href}"><img loading="lazy" src="#{href}" alt="#{h(attachment['name'])}"></a>)
|
|
230
231
|
elsif content_type.start_with?("video/")
|
|
231
232
|
%(<video controls preload="metadata" src="#{href}"></video><a href="#{href}" download>#{h(attachment['name'])}</a>)
|
|
233
|
+
elsif ContentTypes.trace?(content_type)
|
|
234
|
+
%(<a href="#{href}" download>#{h(trace_label(attachment['attempt']))}</a>)
|
|
232
235
|
else
|
|
233
236
|
%(<a href="#{href}">#{h(attachment['name'])}</a>)
|
|
234
237
|
end
|
|
@@ -236,6 +239,10 @@ module RaiderReporter
|
|
|
236
239
|
items.empty? ? "" : %(<div class="a">#{items.join(' ')}</div>)
|
|
237
240
|
end
|
|
238
241
|
|
|
242
|
+
def trace_label(attempt)
|
|
243
|
+
attempt.is_a?(Integer) ? "Trace, attempt #{attempt} (open in Raider Desktop)" : "Trace (open in Raider Desktop)"
|
|
244
|
+
end
|
|
245
|
+
|
|
239
246
|
# Relative paths inside the results folder only, each segment URL-encoded.
|
|
240
247
|
def safe_href(path)
|
|
241
248
|
path = path.to_s
|
|
@@ -4,6 +4,7 @@ require "net/http"
|
|
|
4
4
|
require "uri"
|
|
5
5
|
require "json"
|
|
6
6
|
require "securerandom"
|
|
7
|
+
require_relative "../content_types"
|
|
7
8
|
|
|
8
9
|
module RaiderReporter
|
|
9
10
|
module Writers
|
|
@@ -48,8 +49,9 @@ module RaiderReporter
|
|
|
48
49
|
|
|
49
50
|
private
|
|
50
51
|
|
|
51
|
-
# Keeps the attachments that fit the byte and count budget,
|
|
52
|
-
#
|
|
52
|
+
# Keeps the attachments that fit the byte and count budget: screenshots,
|
|
53
|
+
# logs and other files first, then traces, then videos in whatever room
|
|
54
|
+
# is left. It rebuilds the
|
|
53
55
|
# payload without the rest, so part names stay contiguous. Survivors keep
|
|
54
56
|
# their original order. The local run.json is written from the original
|
|
55
57
|
# payload and keeps all.
|
|
@@ -68,11 +70,13 @@ module RaiderReporter
|
|
|
68
70
|
end))
|
|
69
71
|
end
|
|
70
72
|
|
|
71
|
-
# Identity-keyed set of the attachments to upload.
|
|
72
|
-
# and stop at the first byte overflow;
|
|
73
|
-
#
|
|
73
|
+
# Identity-keyed set of the attachments to upload. Other files come first
|
|
74
|
+
# and stop at the first byte overflow; traces and then videos fill the
|
|
75
|
+
# room left, and one that does not fit is skipped while later ones are
|
|
76
|
+
# still tried.
|
|
74
77
|
def budgeted(entries)
|
|
75
|
-
|
|
78
|
+
traces, rest = entries.partition { |entry| ContentTypes.trace?(entry[:attachment].content_type) }
|
|
79
|
+
videos, others = rest.partition { |entry| ContentTypes.video?(entry[:attachment].content_type) }
|
|
76
80
|
keep = {}.compare_by_identity
|
|
77
81
|
bytes = 0
|
|
78
82
|
others.each do |entry|
|
|
@@ -81,7 +85,7 @@ module RaiderReporter
|
|
|
81
85
|
|
|
82
86
|
keep[entry[:attachment]] = true
|
|
83
87
|
end
|
|
84
|
-
videos.each do |entry|
|
|
88
|
+
(traces + videos).each do |entry|
|
|
85
89
|
break if keep.size >= MAX_UPLOAD_ATTACHMENTS
|
|
86
90
|
|
|
87
91
|
size = size_of(entry)
|
|
@@ -93,10 +97,6 @@ module RaiderReporter
|
|
|
93
97
|
keep
|
|
94
98
|
end
|
|
95
99
|
|
|
96
|
-
def video?(attachment)
|
|
97
|
-
attachment.content_type.to_s.start_with?("video/")
|
|
98
|
-
end
|
|
99
|
-
|
|
100
100
|
def size_of(entry)
|
|
101
101
|
File.size?(entry[:attachment].source_path).to_i
|
|
102
102
|
end
|
data/lib/raider_reporter.rb
CHANGED
|
@@ -56,6 +56,7 @@ module RaiderReporter
|
|
|
56
56
|
@collector = Collector.new(
|
|
57
57
|
max_attachment_bytes: configuration.max_attachment_bytes,
|
|
58
58
|
max_video_bytes: configuration.max_video_bytes,
|
|
59
|
+
max_trace_bytes: configuration.max_trace_bytes,
|
|
59
60
|
warn: ->(message) { warn(message) },
|
|
60
61
|
quarantine: -> { quarantined_keys }
|
|
61
62
|
)
|
|
@@ -66,7 +67,8 @@ module RaiderReporter
|
|
|
66
67
|
@quarantined_keys ||= Quarantine.load(configuration, warn: ->(message) { warn(message) })
|
|
67
68
|
end
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
# attempt: the try of the test that produced the file, for attachments kept per attempt (traces).
|
|
71
|
+
def attach(name, path, type: nil, attempt: nil)
|
|
70
72
|
unless collector&.in_test?
|
|
71
73
|
warn("attach(#{name.inspect}) called outside a running example; ignored")
|
|
72
74
|
return false
|
|
@@ -77,14 +79,14 @@ module RaiderReporter
|
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
content_type = ContentTypes.resolve(type, path)
|
|
80
|
-
limit = attachment_limit(content_type)
|
|
82
|
+
limit = configuration.attachment_limit(content_type)
|
|
81
83
|
if File.size(path.to_s) > limit
|
|
82
84
|
warn("attach(#{name.inspect}): file #{path} is larger than #{limit} bytes; ignored")
|
|
83
85
|
return false
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
collector.attach(Attachment.new(name: name.to_s, source_path: collector.stage(path.to_s),
|
|
87
|
-
content_type: content_type))
|
|
89
|
+
content_type: content_type, attempt: attempt))
|
|
88
90
|
rescue StandardError => e
|
|
89
91
|
warn("attach(#{name.inspect}) failed: #{e.class}: #{e.message}")
|
|
90
92
|
false
|
|
@@ -126,10 +128,6 @@ module RaiderReporter
|
|
|
126
128
|
|
|
127
129
|
private
|
|
128
130
|
|
|
129
|
-
def attachment_limit(content_type)
|
|
130
|
-
content_type.start_with?("video/") ? configuration.max_video_bytes : configuration.max_attachment_bytes
|
|
131
|
-
end
|
|
132
|
-
|
|
133
131
|
# First, so a later reporting problem cannot lose it. A failure here only means
|
|
134
132
|
# no summary, and exec then keeps the command's exit code.
|
|
135
133
|
def write_exec_summary
|