specwrk 0.19.2 → 0.19.3
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 +12 -1
- data/lib/specwrk/store/file_adapter.rb +23 -7
- data/lib/specwrk/store/serializer.rb +3 -3
- data/lib/specwrk/version.rb +1 -1
- data/lib/specwrk/web/endpoints/complete_and_pop.rb +63 -12
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b2eb07bead54ad609b2d8fb821793a60b783485326daeccc092c14343ae96dc0
|
|
4
|
+
data.tar.gz: c41e008f026a61f2748cc8f5cd9c7dc2c257103518aa89fded4c84c9bd2ca976
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f9a7206df73023a2262d9775ff3b1983f403b4630ce54a04f7c4500ab77c51d30df6262604332b37e37fdf21cb98fe39952fcd0287f8b9ded7811a39ac97afe
|
|
7
|
+
data.tar.gz: ffbf1b2583044b4f3e4bdbc7cadb35278f93eb64274905c5869398f5ece9d2854ddad0c75463ac8172d025d8dcc7a92c8486d5d9ef581ad269c875aca5c22420
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
|
-
[Compare](https://github.com/danielwestendorf/specwrk/compare/v0.19.
|
|
4
|
+
[Compare](https://github.com/danielwestendorf/specwrk/compare/v0.19.3...main)
|
|
5
|
+
|
|
6
|
+
## v0.19.3 — 2026-07-26
|
|
7
|
+
[Compare](https://github.com/danielwestendorf/specwrk/compare/v0.19.2...v0.19.3)
|
|
8
|
+
- Retry transient file store read descriptor errors — [@danielwestendorf](https://github.com/danielwestendorf)
|
|
9
|
+
- Complete omitted worker processing examples as failures — [@danielwestendorf](https://github.com/danielwestendorf), [@benjaminwood](https://github.com/benjaminwood)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## v0.19.2 — 2025-12-09
|
|
13
|
+
[Compare](https://github.com/danielwestendorf/specwrk/compare/v0.19.1...v0.19.2)
|
|
14
|
+
- Optimize msgpack usage by using one packer/unpacker — [#180](https://github.com/danielwestendorf/specwrk/pull/180) by [@danielwestendorf](https://github.com/danielwestendorf)
|
|
15
|
+
- Standard style to v3.4 — [#179](https://github.com/danielwestendorf/specwrk/pull/179) by [@danielwestendorf](https://github.com/danielwestendorf)
|
|
5
16
|
- Add msgpack to the docker server — [#178](https://github.com/danielwestendorf/specwrk/pull/178) by [@danielwestendorf](https://github.com/danielwestendorf)
|
|
6
17
|
|
|
7
18
|
## v0.19.1 — 2025-12-08
|
|
@@ -10,6 +10,9 @@ require "specwrk/store/base_adapter"
|
|
|
10
10
|
module Specwrk
|
|
11
11
|
class Store
|
|
12
12
|
class FileAdapter < BaseAdapter
|
|
13
|
+
READ_ATTEMPTS = 3
|
|
14
|
+
READ_RETRY_DELAY = 0.01
|
|
15
|
+
|
|
13
16
|
@work_queue = Queue.new
|
|
14
17
|
@threads = []
|
|
15
18
|
|
|
@@ -91,18 +94,21 @@ module Specwrk
|
|
|
91
94
|
|
|
92
95
|
read_keys.each do |key|
|
|
93
96
|
self.class.schedule_work do
|
|
94
|
-
result_queue.push([key.to_s, read(key)])
|
|
97
|
+
result_queue.push([:ok, key.to_s, read(key)])
|
|
98
|
+
rescue => e
|
|
99
|
+
result_queue.push([:error, key.to_s, e])
|
|
95
100
|
end
|
|
96
101
|
end
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
raw_results = read_keys.length.times.map { result_queue.pop }
|
|
104
|
+
error = raw_results.find { |status, _key, _result| status == :error }
|
|
105
|
+
raise error.last if error
|
|
99
106
|
|
|
100
107
|
results = {}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
next if result.last.nil?
|
|
108
|
+
raw_results.each do |_status, key, content|
|
|
109
|
+
next if content.nil?
|
|
104
110
|
|
|
105
|
-
results[
|
|
111
|
+
results[key] = self.class.serializer.load(content)
|
|
106
112
|
end
|
|
107
113
|
|
|
108
114
|
read_keys.map { |key| [key.to_s, results[key.to_s]] if results.key?(key.to_s) }.compact.to_h # respect order requested in the returned hash
|
|
@@ -140,7 +146,17 @@ module Specwrk
|
|
|
140
146
|
|
|
141
147
|
def read(key)
|
|
142
148
|
filename = filename_for_key key
|
|
143
|
-
|
|
149
|
+
attempts = 0
|
|
150
|
+
|
|
151
|
+
begin
|
|
152
|
+
attempts += 1
|
|
153
|
+
File.binread(filename)
|
|
154
|
+
rescue Errno::EBADF
|
|
155
|
+
raise if attempts >= READ_ATTEMPTS
|
|
156
|
+
|
|
157
|
+
sleep(READ_RETRY_DELAY * attempts)
|
|
158
|
+
retry
|
|
159
|
+
end
|
|
144
160
|
rescue Errno::ENOENT
|
|
145
161
|
nil
|
|
146
162
|
end
|
|
@@ -36,14 +36,14 @@ module Specwrk
|
|
|
36
36
|
|
|
37
37
|
def dump(value)
|
|
38
38
|
packer.tap(&:clear)
|
|
39
|
-
.tap {
|
|
39
|
+
.tap { |msgpack| msgpack.write(value) }
|
|
40
40
|
.then(&:to_s)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def load(payload)
|
|
44
44
|
unpacker.tap(&:reset)
|
|
45
|
-
.tap {
|
|
46
|
-
.then {
|
|
45
|
+
.tap { |msgpack| msgpack.feed(payload) }
|
|
46
|
+
.then { |msgpack| msgpack.read }
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def packer
|
data/lib/specwrk/version.rb
CHANGED
|
@@ -7,12 +7,14 @@ module Specwrk
|
|
|
7
7
|
module Endpoints
|
|
8
8
|
class CompleteAndPop < Popable
|
|
9
9
|
EXAMPLE_STATUSES = %w[passed failed pending]
|
|
10
|
+
SYNTHETIC_FAILURE_CLASS = "Specwrk::WorkerExecutionError"
|
|
10
11
|
|
|
11
12
|
def with_response
|
|
12
13
|
retry_examples # pre-calculate before lock
|
|
14
|
+
omitted_processing_examples # pre-calculate before lock
|
|
13
15
|
|
|
14
16
|
with_lock do
|
|
15
|
-
processing.delete(*(completed_examples.keys + retry_examples.keys))
|
|
17
|
+
processing.delete(*(completed_examples.keys + retry_examples.keys).map(&:to_s))
|
|
16
18
|
pending.merge!(retry_examples)
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -26,24 +28,30 @@ module Specwrk
|
|
|
26
28
|
|
|
27
29
|
private
|
|
28
30
|
|
|
29
|
-
def
|
|
30
|
-
@
|
|
31
|
+
def reported_processing_examples
|
|
32
|
+
@reported_processing_examples ||= payload.map do |example|
|
|
33
|
+
[example[:id], example] if processing_examples_for_payload[example[:id]]
|
|
34
|
+
end.compact.to_h
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
def
|
|
34
|
-
@
|
|
37
|
+
def processing_examples_for_payload
|
|
38
|
+
@processing_examples_for_payload ||= processing.multi_read(*payload.map { |example| example[:id] })
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
def completed_examples
|
|
38
|
-
@completed_examples ||=
|
|
39
|
-
|
|
42
|
+
@completed_examples ||= begin
|
|
43
|
+
reported_completed_examples = reported_processing_examples.map do |id, example|
|
|
44
|
+
next if retry_example?(example)
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
[id, example]
|
|
47
|
+
end.compact.to_h
|
|
48
|
+
|
|
49
|
+
reported_completed_examples.merge(unreported_completed_examples)
|
|
50
|
+
end
|
|
43
51
|
end
|
|
44
52
|
|
|
45
53
|
def retry_examples
|
|
46
|
-
@retry_examples ||=
|
|
54
|
+
@retry_examples ||= reported_processing_examples.map do |id, example|
|
|
47
55
|
next unless retry_example?(example)
|
|
48
56
|
|
|
49
57
|
[id, example]
|
|
@@ -66,7 +74,48 @@ module Specwrk
|
|
|
66
74
|
end
|
|
67
75
|
|
|
68
76
|
def all_example_failure_counts
|
|
69
|
-
@all_example_failure_counts ||= failure_counts.multi_read(*
|
|
77
|
+
@all_example_failure_counts ||= failure_counts.multi_read(*reported_processing_examples.keys)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def omitted_processing_examples
|
|
81
|
+
@omitted_processing_examples ||= processing.to_h.select do |_id, example|
|
|
82
|
+
example[:worker_id].to_s == worker_id && !payload_example_ids.include?(example[:id].to_s)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def unreported_completed_examples
|
|
87
|
+
@unreported_completed_examples ||= omitted_processing_examples.transform_values { |example| synthetic_failure_for(example) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def payload_example_ids
|
|
91
|
+
@payload_example_ids ||= payload.map { |example| example[:id].to_s }.uniq
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def synthetic_failure_for(example)
|
|
95
|
+
finished_at = Time.now
|
|
96
|
+
|
|
97
|
+
{
|
|
98
|
+
id: example[:id],
|
|
99
|
+
full_description: "Specwrk worker execution failed before completing #{example[:id]}",
|
|
100
|
+
status: "failed",
|
|
101
|
+
file_path: example[:file_path],
|
|
102
|
+
line_number: line_number_for(example),
|
|
103
|
+
started_at: finished_at.iso8601(6),
|
|
104
|
+
finished_at: finished_at.iso8601(6),
|
|
105
|
+
run_time: 0.0,
|
|
106
|
+
exception: {
|
|
107
|
+
class: SYNTHETIC_FAILURE_CLASS,
|
|
108
|
+
message: "Worker #{worker_id} claimed this example but did not submit a completion result. Check worker stderr/stdout for the underlying RSpec output.",
|
|
109
|
+
backtrace: []
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def line_number_for(example)
|
|
115
|
+
return example[:line_number] if example[:line_number]
|
|
116
|
+
|
|
117
|
+
match = example[:id].to_s.match(/:(\d+)\z/)
|
|
118
|
+
match[1].to_i if match
|
|
70
119
|
end
|
|
71
120
|
|
|
72
121
|
def completed_examples_status_counts
|
|
@@ -87,7 +136,9 @@ module Specwrk
|
|
|
87
136
|
end
|
|
88
137
|
|
|
89
138
|
def run_time_data
|
|
90
|
-
@run_time_data ||= payload.map { |example| [example[:id], example[:run_time]] }.to_h
|
|
139
|
+
@run_time_data ||= payload.map { |example| [example[:id], example[:run_time]] }.to_h.merge(
|
|
140
|
+
unreported_completed_examples.transform_values { |example| example[:run_time] }
|
|
141
|
+
)
|
|
91
142
|
end
|
|
92
143
|
end
|
|
93
144
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: specwrk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.19.
|
|
4
|
+
version: 0.19.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Westendorf
|
|
@@ -274,7 +274,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
274
274
|
- !ruby/object:Gem::Version
|
|
275
275
|
version: '0'
|
|
276
276
|
requirements: []
|
|
277
|
-
rubygems_version:
|
|
277
|
+
rubygems_version: 4.0.10
|
|
278
278
|
specification_version: 4
|
|
279
279
|
summary: Parallel rspec test runner from a queue of pending jobs.
|
|
280
280
|
test_files: []
|