turbo_tests 1.2.0 → 1.2.5
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/.github/workflows/snyk_ruby-analysis.yml +33 -0
- data/.gitignore +57 -0
- data/.rspec +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +11 -11
- data/README.md +6 -18
- data/fixtures/rspec/errors_outside_of_examples_spec.rb +5 -0
- data/fixtures/rspec/pending_exceptions_spec.rb +15 -0
- data/lib/turbo_tests.rb +21 -20
- data/lib/turbo_tests/cli.rb +1 -1
- data/lib/turbo_tests/json_rows_formatter.rb +79 -29
- data/lib/turbo_tests/reporter.rb +27 -4
- data/lib/turbo_tests/runner.rb +116 -44
- data/lib/turbo_tests/version.rb +1 -1
- data/lib/utils/hash_extension.rb +9 -0
- data/turbo_tests.gemspec +3 -3
- metadata +11 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8a0dc37755b687ea503d265d864eb959d2bda3668e3f21c4b09af48e6fb852a
|
4
|
+
data.tar.gz: 8399b27819ae06f123b0695754fe074cb8819d385cd2a4d932c12b9f30a88124
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cae6d818935454f40984af484fa8d87763732b578a4bb901679434c40a0d5dc152c671726523877247375c4572849c907e4190faab6e0681abb3b2d8db50e1bd
|
7
|
+
data.tar.gz: 41a44281ddb35a3440eaa633bafecd7e9bb585acf132e69180a521e68148a9feb10121ac1a7bd38c38892d56c0ba326215bacd590380355ece8b02715667697b
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# A sample workflow which checks out your Infrastructure as Code Configuration files,
|
2
|
+
# such as Kubernetes, Helm & Terraform and scans them for any security issues.
|
3
|
+
# The results are then uploaded to GitHub Security Code Scanning
|
4
|
+
#
|
5
|
+
# For more examples, including how to limit scans to only high-severity issues
|
6
|
+
# and fail PR checks, see https://github.com/snyk/actions/
|
7
|
+
|
8
|
+
name: Snyk Ruby
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
# The branches below must be a subset of the branches above
|
15
|
+
branches: [ master ]
|
16
|
+
schedule:
|
17
|
+
- cron: '0 0 * * 0'
|
18
|
+
|
19
|
+
jobs:
|
20
|
+
snyk:
|
21
|
+
runs-on: ubuntu-latest
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
- name: Run Snyk to check configuration files for security issues
|
25
|
+
# Snyk can be used to break the build when it detects security issues.
|
26
|
+
# In this case we want to upload the issues to GitHub Code Scanning
|
27
|
+
continue-on-error: true
|
28
|
+
uses: snyk/actions/ruby@master
|
29
|
+
env:
|
30
|
+
# In order to use the Snyk Action you will need to have a Snyk API token.
|
31
|
+
# More details in https://github.com/snyk/actions#getting-your-snyk-token
|
32
|
+
# or you can signup for free at https://snyk.io/login
|
33
|
+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
data/.gitignore
CHANGED
@@ -9,3 +9,60 @@
|
|
9
9
|
|
10
10
|
# rspec failure tracking
|
11
11
|
.rspec_status
|
12
|
+
|
13
|
+
|
14
|
+
# Created by https://www.toptal.com/developers/gitignore/api/macos,vim
|
15
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,vim
|
16
|
+
|
17
|
+
### macOS ###
|
18
|
+
# General
|
19
|
+
.DS_Store
|
20
|
+
.AppleDouble
|
21
|
+
.LSOverride
|
22
|
+
|
23
|
+
# Icon must end with two \r
|
24
|
+
Icon
|
25
|
+
|
26
|
+
|
27
|
+
# Thumbnails
|
28
|
+
._*
|
29
|
+
|
30
|
+
# Files that might appear in the root of a volume
|
31
|
+
.DocumentRevisions-V100
|
32
|
+
.fseventsd
|
33
|
+
.Spotlight-V100
|
34
|
+
.TemporaryItems
|
35
|
+
.Trashes
|
36
|
+
.VolumeIcon.icns
|
37
|
+
.com.apple.timemachine.donotpresent
|
38
|
+
|
39
|
+
# Directories potentially created on remote AFP share
|
40
|
+
.AppleDB
|
41
|
+
.AppleDesktop
|
42
|
+
Network Trash Folder
|
43
|
+
Temporary Items
|
44
|
+
.apdisk
|
45
|
+
|
46
|
+
### Vim ###
|
47
|
+
# Swap
|
48
|
+
[._]*.s[a-v][a-z]
|
49
|
+
!*.svg # comment out if you don't need vector files
|
50
|
+
[._]*.sw[a-p]
|
51
|
+
[._]s[a-rt-v][a-z]
|
52
|
+
[._]ss[a-gi-z]
|
53
|
+
[._]sw[a-p]
|
54
|
+
|
55
|
+
# Session
|
56
|
+
Session.vim
|
57
|
+
Sessionx.vim
|
58
|
+
|
59
|
+
# Temporary
|
60
|
+
.netrwhist
|
61
|
+
*~
|
62
|
+
# Auto-generated tag files
|
63
|
+
tags
|
64
|
+
# Persistent undo
|
65
|
+
[._]*.un~
|
66
|
+
|
67
|
+
# End of https://www.toptal.com/developers/gitignore/api/macos,vim
|
68
|
+
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
turbo_tests (1.2.
|
4
|
+
turbo_tests (1.2.5)
|
5
5
|
bundler
|
6
6
|
parallel_tests (~> 3.3)
|
7
|
-
rspec (~> 3.10
|
7
|
+
rspec (~> 3.10)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
@@ -13,32 +13,32 @@ GEM
|
|
13
13
|
diff-lcs (1.4.4)
|
14
14
|
method_source (1.0.0)
|
15
15
|
parallel (1.20.1)
|
16
|
-
parallel_tests (3.
|
16
|
+
parallel_tests (3.7.0)
|
17
17
|
parallel
|
18
|
-
pry (0.
|
18
|
+
pry (0.14.1)
|
19
19
|
coderay (~> 1.1)
|
20
20
|
method_source (~> 1.0)
|
21
|
-
rake (
|
21
|
+
rake (13.0.6)
|
22
22
|
rspec (3.10.0)
|
23
23
|
rspec-core (~> 3.10.0)
|
24
24
|
rspec-expectations (~> 3.10.0)
|
25
25
|
rspec-mocks (~> 3.10.0)
|
26
|
-
rspec-core (3.10.
|
26
|
+
rspec-core (3.10.1)
|
27
27
|
rspec-support (~> 3.10.0)
|
28
|
-
rspec-expectations (3.10.
|
28
|
+
rspec-expectations (3.10.1)
|
29
29
|
diff-lcs (>= 1.2.0, < 2.0)
|
30
30
|
rspec-support (~> 3.10.0)
|
31
|
-
rspec-mocks (3.10.
|
31
|
+
rspec-mocks (3.10.2)
|
32
32
|
diff-lcs (>= 1.2.0, < 2.0)
|
33
33
|
rspec-support (~> 3.10.0)
|
34
|
-
rspec-support (3.10.
|
34
|
+
rspec-support (3.10.2)
|
35
35
|
|
36
36
|
PLATFORMS
|
37
37
|
ruby
|
38
38
|
|
39
39
|
DEPENDENCIES
|
40
|
-
pry (~> 0.
|
41
|
-
rake (~>
|
40
|
+
pry (~> 0.14)
|
41
|
+
rake (~> 13.0)
|
42
42
|
turbo_tests!
|
43
43
|
|
44
44
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+

|
2
|
+
|
1
3
|
# TurboTests
|
2
4
|
|
3
5
|
Runner for [`grosser/parallel_tests`](https://github.com/grosser/parallel_tests) with incremental summarized output. Based on [Discourse](https://github.com/discourse/discourse/blob/6b9784cf8a18636bce281a7e4d18e65a0cbc6290/lib/turbo_tests.rb) and [RubyGems](https://github.com/rubygems/rubygems/tree/390335ceb351668cd433bd5bb9823dd021f82533/bundler/tool) work in this area.
|
@@ -11,23 +13,17 @@ This feature [doesn't fit vision of `parallel_tests` author](https://github.com/
|
|
11
13
|
```bash
|
12
14
|
|
13
15
|
$ bundle exec rake parallel_tests:spec[^spec/search]
|
14
|
-
|
15
|
-
...........................................................................................................................................................................................
|
16
|
-
...........................................................................................................................................................................................
|
16
|
+
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
17
17
|
|
18
18
|
Finished in 1 minute 6.92 seconds (files took 6.95 seconds to load)
|
19
19
|
2616 examples, 0 failures
|
20
20
|
|
21
|
-
.........................................................................................................................................F
|
22
|
-
.......................................................................................F...................................................................................................
|
23
|
-
...........................................................................................................................................................................................
|
21
|
+
.........................................................................................................................................F........................................................................................................................................F..............................................................................................................................................................................................................................................................................................
|
24
22
|
|
25
23
|
Finished in 1 minute 35.05 seconds (files took 6.26 seconds to load)
|
26
24
|
2158 examples, 2 failures
|
27
25
|
|
28
|
-
|
29
|
-
...........................................................................................................................................................................................
|
30
|
-
...........................................................................................................................................................................................
|
26
|
+
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
31
27
|
|
32
28
|
Finished in 1 minute 35.05 seconds (files took 6.26 seconds to load)
|
33
29
|
2158 examples, 0 failures
|
@@ -37,15 +33,7 @@ Finished in 1 minute 35.05 seconds (files took 6.26 seconds to load)
|
|
37
33
|
|
38
34
|
```bash
|
39
35
|
$ bundle exec turbo_tests
|
40
|
-
|
41
|
-
...........................................................................................................................................................................................
|
42
|
-
...........................................................................................................................................................................................
|
43
|
-
.........................................................................................................................................F.................................................
|
44
|
-
.......................................................................................F...................................................................................................
|
45
|
-
...........................................................................................................................................................................................
|
46
|
-
...........................................................................................................................................................................................
|
47
|
-
...........................................................................................................................................................................................
|
48
|
-
...........................................................................................................................................................................................
|
36
|
+
..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F........................................................................................................................................F..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
|
49
37
|
|
50
38
|
Finished in 2 minute 25.15 seconds (files took 0 seconds to load)
|
51
39
|
6873 examples, 2 failures
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe "Fixture of spec file with pending failed examples" do
|
2
|
+
it "is implemented but skipped with 'pending'" do
|
3
|
+
pending("TODO: skipped with 'pending'")
|
4
|
+
|
5
|
+
expect(2).to eq(3)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "is implemented but skipped with 'skip'", skip: "TODO: skipped with 'skip'" do
|
9
|
+
expect(100).to eq(500)
|
10
|
+
end
|
11
|
+
|
12
|
+
xit "is implemented but skipped with 'xit'" do
|
13
|
+
expect(1).to eq(42)
|
14
|
+
end
|
15
|
+
end
|
data/lib/turbo_tests.rb
CHANGED
@@ -23,28 +23,29 @@ module TurboTests
|
|
23
23
|
klass =
|
24
24
|
Class.new(FakeException) {
|
25
25
|
define_singleton_method(:name) do
|
26
|
-
obj[
|
26
|
+
obj[:class_name]
|
27
27
|
end
|
28
28
|
}
|
29
29
|
|
30
30
|
klass.new(
|
31
|
-
obj[
|
32
|
-
obj[
|
33
|
-
FakeException.from_obj(obj[
|
31
|
+
obj[:backtrace],
|
32
|
+
obj[:message],
|
33
|
+
FakeException.from_obj(obj[:cause])
|
34
34
|
)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
FakeExecutionResult = Struct.new(:example_skipped?, :pending_message, :status, :pending_fixed?, :exception)
|
39
|
+
FakeExecutionResult = Struct.new(:example_skipped?, :pending_message, :status, :pending_fixed?, :exception, :pending_exception)
|
40
40
|
class FakeExecutionResult
|
41
41
|
def self.from_obj(obj)
|
42
42
|
new(
|
43
|
-
obj[
|
44
|
-
obj[
|
45
|
-
obj[
|
46
|
-
obj[
|
47
|
-
FakeException.from_obj(obj[
|
43
|
+
obj[:example_skipped?],
|
44
|
+
obj[:pending_message],
|
45
|
+
obj[:status].to_sym,
|
46
|
+
obj[:pending_fixed?],
|
47
|
+
FakeException.from_obj(obj[:exception]),
|
48
|
+
FakeException.from_obj(obj[:exception])
|
48
49
|
)
|
49
50
|
end
|
50
51
|
end
|
@@ -52,24 +53,24 @@ module TurboTests
|
|
52
53
|
FakeExample = Struct.new(:execution_result, :location, :description, :full_description, :metadata, :location_rerun_argument)
|
53
54
|
class FakeExample
|
54
55
|
def self.from_obj(obj)
|
55
|
-
metadata = obj[
|
56
|
+
metadata = obj[:metadata]
|
56
57
|
|
57
|
-
metadata[
|
58
|
+
metadata[:shared_group_inclusion_backtrace].map! do |frame|
|
58
59
|
RSpec::Core::SharedExampleGroupInclusionStackFrame.new(
|
59
|
-
frame[
|
60
|
-
frame[
|
60
|
+
frame[:shared_group_name],
|
61
|
+
frame[:inclusion_location]
|
61
62
|
)
|
62
63
|
end
|
63
64
|
|
64
|
-
metadata[:shared_group_inclusion_backtrace] = metadata.delete(
|
65
|
+
metadata[:shared_group_inclusion_backtrace] = metadata.delete(:shared_group_inclusion_backtrace)
|
65
66
|
|
66
67
|
new(
|
67
|
-
FakeExecutionResult.from_obj(obj[
|
68
|
-
obj[
|
69
|
-
obj[
|
70
|
-
obj[
|
68
|
+
FakeExecutionResult.from_obj(obj[:execution_result]),
|
69
|
+
obj[:location],
|
70
|
+
obj[:description],
|
71
|
+
obj[:full_description],
|
71
72
|
metadata,
|
72
|
-
obj[
|
73
|
+
obj[:location_rerun_argument]
|
73
74
|
)
|
74
75
|
end
|
75
76
|
|
data/lib/turbo_tests/cli.rb
CHANGED
@@ -17,7 +17,7 @@ module TurboTests
|
|
17
17
|
fail_fast = nil
|
18
18
|
|
19
19
|
OptionParser.new { |opts|
|
20
|
-
opts.banner =
|
20
|
+
opts.banner = <<~BANNER
|
21
21
|
Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('1', '2', '3', ...).
|
22
22
|
|
23
23
|
Uses parallel_tests under the hood, but reports test results incrementally. Based on Discourse and RubyGems work in this area.
|
@@ -22,10 +22,14 @@ module TurboTests
|
|
22
22
|
class JsonRowsFormatter
|
23
23
|
RSpec::Core::Formatters.register(
|
24
24
|
self,
|
25
|
+
:start,
|
25
26
|
:close,
|
26
27
|
:example_failed,
|
27
28
|
:example_passed,
|
28
29
|
:example_pending,
|
30
|
+
:example_group_started,
|
31
|
+
:example_group_finished,
|
32
|
+
:message,
|
29
33
|
:seed
|
30
34
|
)
|
31
35
|
|
@@ -35,37 +39,65 @@ module TurboTests
|
|
35
39
|
@output = output
|
36
40
|
end
|
37
41
|
|
42
|
+
def start(notification)
|
43
|
+
output_row(
|
44
|
+
type: :load_summary,
|
45
|
+
summary: load_summary_to_json(notification)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def example_group_started(notification)
|
50
|
+
output_row(
|
51
|
+
type: :group_started,
|
52
|
+
group: group_to_json(notification)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def example_group_finished(notification)
|
57
|
+
output_row(
|
58
|
+
type: :group_finished,
|
59
|
+
group: group_to_json(notification)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
38
63
|
def example_passed(notification)
|
39
64
|
output_row(
|
40
|
-
|
41
|
-
|
65
|
+
type: :example_passed,
|
66
|
+
example: example_to_json(notification.example)
|
42
67
|
)
|
43
68
|
end
|
44
69
|
|
45
70
|
def example_pending(notification)
|
46
71
|
output_row(
|
47
|
-
|
48
|
-
|
72
|
+
type: :example_pending,
|
73
|
+
example: example_to_json(notification.example)
|
49
74
|
)
|
50
75
|
end
|
51
76
|
|
52
77
|
def example_failed(notification)
|
53
78
|
output_row(
|
54
|
-
|
55
|
-
|
79
|
+
type: :example_failed,
|
80
|
+
example: example_to_json(notification.example)
|
56
81
|
)
|
57
82
|
end
|
58
83
|
|
59
84
|
def seed(notification)
|
60
85
|
output_row(
|
61
|
-
|
62
|
-
|
86
|
+
type: :seed,
|
87
|
+
seed: notification.seed
|
63
88
|
)
|
64
89
|
end
|
65
90
|
|
66
91
|
def close(notification)
|
67
92
|
output_row(
|
68
|
-
|
93
|
+
type: :close
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def message(notification)
|
98
|
+
output_row(
|
99
|
+
type: :message,
|
100
|
+
message: notification.message
|
69
101
|
)
|
70
102
|
end
|
71
103
|
|
@@ -74,47 +106,65 @@ module TurboTests
|
|
74
106
|
def exception_to_json(exception)
|
75
107
|
if exception
|
76
108
|
{
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
109
|
+
class_name: exception.class.name.to_s,
|
110
|
+
backtrace: exception.backtrace,
|
111
|
+
message: exception.message,
|
112
|
+
cause: exception_to_json(exception.cause)
|
81
113
|
}
|
82
114
|
end
|
83
115
|
end
|
84
116
|
|
85
117
|
def execution_result_to_json(result)
|
86
118
|
{
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
119
|
+
example_skipped?: result.example_skipped?,
|
120
|
+
pending_message: result.pending_message,
|
121
|
+
status: result.status,
|
122
|
+
pending_fixed?: result.pending_fixed?,
|
123
|
+
exception: exception_to_json(result.exception || result.pending_exception)
|
92
124
|
}
|
93
125
|
end
|
94
126
|
|
95
127
|
def stack_frame_to_json(frame)
|
96
128
|
{
|
97
|
-
|
98
|
-
|
129
|
+
shared_group_name: frame.shared_group_name,
|
130
|
+
inclusion_location: frame.inclusion_location
|
99
131
|
}
|
100
132
|
end
|
101
133
|
|
102
134
|
def example_to_json(example)
|
103
135
|
{
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
example
|
136
|
+
execution_result: execution_result_to_json(example.execution_result),
|
137
|
+
location: example.location,
|
138
|
+
description: example.description,
|
139
|
+
full_description: example.full_description,
|
140
|
+
metadata: {
|
141
|
+
shared_group_inclusion_backtrace:
|
142
|
+
example
|
143
|
+
.metadata[:shared_group_inclusion_backtrace]
|
144
|
+
.map { |frame| stack_frame_to_json(frame) }
|
111
145
|
},
|
112
|
-
|
146
|
+
location_rerun_argument: example.location_rerun_argument
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
def load_summary_to_json(notification)
|
151
|
+
{
|
152
|
+
count: notification.count,
|
153
|
+
load_time: notification.load_time,
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def group_to_json(notification)
|
158
|
+
{
|
159
|
+
group: {
|
160
|
+
description: notification.group.description
|
161
|
+
}
|
113
162
|
}
|
114
163
|
end
|
115
164
|
|
116
165
|
def output_row(obj)
|
117
|
-
output.puts
|
166
|
+
output.puts(obj.to_json)
|
167
|
+
output.flush
|
118
168
|
end
|
119
169
|
end
|
120
170
|
end
|
data/lib/turbo_tests/reporter.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module TurboTests
|
4
4
|
class Reporter
|
5
|
+
attr_writer :load_time
|
6
|
+
|
5
7
|
def self.from_config(formatter_config, start_time)
|
6
8
|
reporter = new(start_time)
|
7
9
|
|
@@ -9,7 +11,7 @@ module TurboTests
|
|
9
11
|
name, outputs = config.values_at(:name, :outputs)
|
10
12
|
|
11
13
|
outputs.map! do |filename|
|
12
|
-
filename == "-" ?
|
14
|
+
filename == "-" ? $stdout : File.open(filename, "w")
|
13
15
|
end
|
14
16
|
|
15
17
|
reporter.add(name, outputs)
|
@@ -26,7 +28,10 @@ module TurboTests
|
|
26
28
|
@pending_examples = []
|
27
29
|
@failed_examples = []
|
28
30
|
@all_examples = []
|
31
|
+
@messages = []
|
29
32
|
@start_time = start_time
|
33
|
+
@load_time = 0
|
34
|
+
@errors_outside_of_examples_count = 0
|
30
35
|
end
|
31
36
|
|
32
37
|
def add(name, outputs)
|
@@ -45,6 +50,14 @@ module TurboTests
|
|
45
50
|
end
|
46
51
|
end
|
47
52
|
|
53
|
+
def group_started(notification)
|
54
|
+
delegate_to_formatters(:example_group_started, notification)
|
55
|
+
end
|
56
|
+
|
57
|
+
def group_finished
|
58
|
+
delegate_to_formatters(:example_group_finished, nil)
|
59
|
+
end
|
60
|
+
|
48
61
|
def example_passed(example)
|
49
62
|
delegate_to_formatters(:example_passed, example.notification)
|
50
63
|
|
@@ -65,8 +78,18 @@ module TurboTests
|
|
65
78
|
@failed_examples << example
|
66
79
|
end
|
67
80
|
|
81
|
+
def message(message)
|
82
|
+
delegate_to_formatters(:message, RSpec::Core::Notifications::MessageNotification.new(message))
|
83
|
+
@messages << message
|
84
|
+
end
|
85
|
+
|
86
|
+
def error_outside_of_examples
|
87
|
+
@errors_outside_of_examples_count += 1
|
88
|
+
end
|
89
|
+
|
68
90
|
def finish
|
69
|
-
|
91
|
+
# SEE: https://bit.ly/2NP87Cz
|
92
|
+
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
70
93
|
|
71
94
|
delegate_to_formatters(:start_dump,
|
72
95
|
RSpec::Core::Notifications::NullNotification)
|
@@ -84,8 +107,8 @@ module TurboTests
|
|
84
107
|
@all_examples,
|
85
108
|
@failed_examples,
|
86
109
|
@pending_examples,
|
87
|
-
|
88
|
-
|
110
|
+
@load_time,
|
111
|
+
@errors_outside_of_examples_count
|
89
112
|
))
|
90
113
|
delegate_to_formatters(:close,
|
91
114
|
RSpec::Core::Notifications::NullNotification)
|
data/lib/turbo_tests/runner.rb
CHANGED
@@ -3,17 +3,27 @@
|
|
3
3
|
require "json"
|
4
4
|
require "parallel_tests/rspec/runner"
|
5
5
|
|
6
|
+
require_relative "../utils/hash_extension"
|
7
|
+
|
6
8
|
module TurboTests
|
7
9
|
class Runner
|
10
|
+
using CoreExtensions
|
11
|
+
|
8
12
|
def self.run(opts = {})
|
9
13
|
files = opts[:files]
|
10
14
|
formatters = opts[:formatters]
|
11
15
|
tags = opts[:tags]
|
12
|
-
|
16
|
+
|
17
|
+
# SEE: https://bit.ly/2NP87Cz
|
18
|
+
start_time = opts.fetch(:start_time) { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
13
19
|
verbose = opts.fetch(:verbose, false)
|
14
20
|
fail_fast = opts.fetch(:fail_fast, nil)
|
15
21
|
count = opts.fetch(:count, nil)
|
16
22
|
|
23
|
+
if verbose
|
24
|
+
STDERR.puts "VERBOSE"
|
25
|
+
end
|
26
|
+
|
17
27
|
reporter = Reporter.from_config(formatters, start_time)
|
18
28
|
|
19
29
|
new(
|
@@ -33,12 +43,13 @@ module TurboTests
|
|
33
43
|
@verbose = opts[:verbose]
|
34
44
|
@fail_fast = opts[:fail_fast]
|
35
45
|
@count = opts[:count]
|
36
|
-
|
46
|
+
@load_time = 0
|
47
|
+
@load_count = 0
|
37
48
|
@failure_count = 0
|
38
|
-
@runtime_log = "tmp/parallel_runtime_rspec.log"
|
39
49
|
|
40
50
|
@messages = Queue.new
|
41
51
|
@threads = []
|
52
|
+
@error = false
|
42
53
|
end
|
43
54
|
|
44
55
|
def run
|
@@ -47,17 +58,33 @@ module TurboTests
|
|
47
58
|
ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size
|
48
59
|
].min
|
49
60
|
|
61
|
+
use_runtime_info = @files == ["spec"]
|
62
|
+
|
63
|
+
group_opts = {}
|
64
|
+
|
65
|
+
if use_runtime_info
|
66
|
+
group_opts[:runtime_log] = "tmp/turbo_rspec_runtime.log"
|
67
|
+
else
|
68
|
+
group_opts[:group_by] = :filesize
|
69
|
+
end
|
70
|
+
|
50
71
|
tests_in_groups =
|
51
72
|
ParallelTests::RSpec::Runner.tests_in_groups(
|
52
73
|
@files,
|
53
74
|
@num_processes,
|
54
|
-
|
75
|
+
**group_opts
|
55
76
|
)
|
56
77
|
|
78
|
+
setup_tmp_dir
|
79
|
+
|
80
|
+
subprocess_opts = {
|
81
|
+
record_runtime: use_runtime_info
|
82
|
+
}
|
83
|
+
|
57
84
|
report_number_of_tests(tests_in_groups)
|
58
85
|
|
59
|
-
tests_in_groups.
|
60
|
-
start_regular_subprocess(tests, process_id + 1)
|
86
|
+
wait_threads = tests_in_groups.map.with_index do |tests, process_id|
|
87
|
+
start_regular_subprocess(tests, process_id + 1, **subprocess_opts)
|
61
88
|
end
|
62
89
|
|
63
90
|
handle_messages
|
@@ -66,38 +93,64 @@ module TurboTests
|
|
66
93
|
|
67
94
|
@threads.each(&:join)
|
68
95
|
|
69
|
-
@reporter.failed_examples.empty?
|
96
|
+
@reporter.failed_examples.empty? && wait_threads.map(&:value).all?(&:success?)
|
70
97
|
end
|
71
98
|
|
72
|
-
|
99
|
+
private
|
100
|
+
|
101
|
+
def setup_tmp_dir
|
102
|
+
begin
|
103
|
+
FileUtils.rm_r("tmp/test-pipes")
|
104
|
+
rescue Errno::ENOENT
|
105
|
+
end
|
73
106
|
|
74
|
-
|
107
|
+
FileUtils.mkdir_p("tmp/test-pipes/")
|
108
|
+
end
|
109
|
+
|
110
|
+
def start_regular_subprocess(tests, process_id, **opts)
|
75
111
|
start_subprocess(
|
76
112
|
{"TEST_ENV_NUMBER" => process_id.to_s},
|
77
113
|
@tags.map { |tag| "--tag=#{tag}" },
|
78
114
|
tests,
|
79
|
-
process_id
|
115
|
+
process_id,
|
116
|
+
**opts
|
80
117
|
)
|
81
118
|
end
|
82
119
|
|
83
|
-
def start_subprocess(env, extra_args, tests, process_id)
|
120
|
+
def start_subprocess(env, extra_args, tests, process_id, record_runtime:)
|
84
121
|
if tests.empty?
|
85
122
|
@messages << {
|
86
|
-
|
87
|
-
|
123
|
+
type: "exit",
|
124
|
+
process_id: process_id
|
88
125
|
}
|
89
126
|
else
|
90
|
-
|
91
|
-
|
92
|
-
|
127
|
+
tmp_filename = "tmp/test-pipes/subprocess-#{process_id}"
|
128
|
+
|
129
|
+
begin
|
130
|
+
File.mkfifo(tmp_filename)
|
131
|
+
rescue Errno::EEXIST
|
132
|
+
end
|
133
|
+
|
134
|
+
env["RUBYOPT"] = ["-I#{File.expand_path("..", __dir__)}", ENV["RUBYOPT"]].compact.join(" ")
|
135
|
+
env["RSPEC_SILENCE_FILTER_ANNOUNCEMENTS"] = "1"
|
136
|
+
|
137
|
+
record_runtime_options =
|
138
|
+
if record_runtime
|
139
|
+
[
|
140
|
+
"--format", "ParallelTests::RSpec::RuntimeLogger",
|
141
|
+
"--out", "tmp/turbo_rspec_runtime.log",
|
142
|
+
]
|
143
|
+
else
|
144
|
+
[]
|
145
|
+
end
|
93
146
|
|
94
147
|
command = [
|
95
148
|
ENV["BUNDLE_BIN_PATH"], "exec", "rspec",
|
96
149
|
*extra_args,
|
97
|
-
"--seed", rand(
|
98
|
-
"--format", "ParallelTests::RSpec::RuntimeLogger",
|
99
|
-
"--out", @runtime_log,
|
150
|
+
"--seed", rand(0xFFFF).to_s,
|
100
151
|
"--format", "TurboTests::JsonRowsFormatter",
|
152
|
+
"--out", tmp_filename,
|
153
|
+
*record_runtime_options,
|
101
154
|
*tests
|
102
155
|
]
|
103
156
|
|
@@ -110,29 +163,33 @@ module TurboTests
|
|
110
163
|
STDERR.puts "Process #{process_id}: #{command_str}"
|
111
164
|
end
|
112
165
|
|
113
|
-
|
166
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(env, *command)
|
167
|
+
stdin.close
|
114
168
|
|
115
169
|
@threads <<
|
116
|
-
Thread.new
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
message = result.shift
|
125
|
-
next unless message
|
126
|
-
|
127
|
-
message = JSON.parse(message)
|
128
|
-
message["process_id"] = process_id
|
129
|
-
@messages << message
|
170
|
+
Thread.new do
|
171
|
+
File.open(tmp_filename) do |fd|
|
172
|
+
fd.each_line do |line|
|
173
|
+
message = JSON.parse(line, symbolize_names: true)
|
174
|
+
|
175
|
+
message[:process_id] = process_id
|
176
|
+
@messages << message
|
177
|
+
end
|
130
178
|
end
|
131
179
|
|
132
|
-
@messages << {
|
133
|
-
|
180
|
+
@messages << {type: "exit", process_id: process_id}
|
181
|
+
end
|
134
182
|
|
183
|
+
@threads << start_copy_thread(stdout, STDOUT)
|
135
184
|
@threads << start_copy_thread(stderr, STDERR)
|
185
|
+
|
186
|
+
@threads << Thread.new {
|
187
|
+
unless wait_thr.value.success?
|
188
|
+
@messages << {type: "error"}
|
189
|
+
end
|
190
|
+
}
|
191
|
+
|
192
|
+
wait_thr
|
136
193
|
end
|
137
194
|
end
|
138
195
|
|
@@ -141,6 +198,7 @@ module TurboTests
|
|
141
198
|
loop do
|
142
199
|
msg = src.readpartial(4096)
|
143
200
|
rescue EOFError
|
201
|
+
src.close
|
144
202
|
break
|
145
203
|
else
|
146
204
|
dst.write(msg)
|
@@ -153,31 +211,47 @@ module TurboTests
|
|
153
211
|
|
154
212
|
loop do
|
155
213
|
message = @messages.pop
|
156
|
-
case message[
|
214
|
+
case message[:type]
|
157
215
|
when "example_passed"
|
158
|
-
example = FakeExample.from_obj(message[
|
216
|
+
example = FakeExample.from_obj(message[:example])
|
159
217
|
@reporter.example_passed(example)
|
218
|
+
when "group_started"
|
219
|
+
@reporter.group_started(message[:group].to_struct)
|
220
|
+
when "group_finished"
|
221
|
+
@reporter.group_finished
|
160
222
|
when "example_pending"
|
161
|
-
example = FakeExample.from_obj(message[
|
223
|
+
example = FakeExample.from_obj(message[:example])
|
162
224
|
@reporter.example_pending(example)
|
225
|
+
when "load_summary"
|
226
|
+
message = message[:summary]
|
227
|
+
# NOTE: notifications order and content is not guaranteed hence the fetch
|
228
|
+
# and count increment tracking to get the latest accumulated load time
|
229
|
+
@reporter.load_time = message[:load_time] if message.fetch(:count, 0) > @load_count
|
163
230
|
when "example_failed"
|
164
|
-
example = FakeExample.from_obj(message[
|
231
|
+
example = FakeExample.from_obj(message[:example])
|
165
232
|
@reporter.example_failed(example)
|
166
233
|
@failure_count += 1
|
167
234
|
if fail_fast_met
|
168
235
|
@threads.each(&:kill)
|
169
236
|
break
|
170
237
|
end
|
238
|
+
when "message"
|
239
|
+
@reporter.message(message[:message])
|
171
240
|
when "seed"
|
172
241
|
when "close"
|
242
|
+
when "error"
|
243
|
+
@reporter.error_outside_of_examples
|
244
|
+
@error = true
|
173
245
|
when "exit"
|
174
246
|
exited += 1
|
175
247
|
if exited == @num_processes
|
176
248
|
break
|
177
249
|
end
|
178
250
|
else
|
179
|
-
|
251
|
+
STDERR.puts("Unhandled message in main process: #{message}")
|
180
252
|
end
|
253
|
+
|
254
|
+
STDOUT.flush
|
181
255
|
end
|
182
256
|
rescue Interrupt
|
183
257
|
end
|
@@ -186,14 +260,12 @@ module TurboTests
|
|
186
260
|
!@fail_fast.nil? && @fail_fast >= @failure_count
|
187
261
|
end
|
188
262
|
|
189
|
-
private
|
190
|
-
|
191
263
|
def report_number_of_tests(groups)
|
192
264
|
name = ParallelTests::RSpec::Runner.test_file_name
|
193
265
|
|
194
266
|
num_processes = groups.size
|
195
267
|
num_tests = groups.map(&:size).sum
|
196
|
-
tests_per_process = (num_processes == 0 ? 0 : num_tests / num_processes)
|
268
|
+
tests_per_process = (num_processes == 0 ? 0 : num_tests.to_f / num_processes).round
|
197
269
|
|
198
270
|
puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{tests_per_process} #{name}s per process"
|
199
271
|
end
|
data/lib/turbo_tests/version.rb
CHANGED
data/turbo_tests.gemspec
CHANGED
@@ -14,12 +14,12 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.metadata["source_code_uri"] = "https://github.com/serpapi/turbo_tests"
|
15
15
|
spec.metadata["changelog_uri"] = "https://github.com/serpapi/turbo_tests/releases"
|
16
16
|
|
17
|
-
spec.required_ruby_version = ">= 2.4
|
17
|
+
spec.required_ruby_version = ">= 2.4"
|
18
18
|
|
19
|
-
spec.add_dependency "rspec", "~> 3.10
|
19
|
+
spec.add_dependency "rspec", "~> 3.10"
|
20
20
|
spec.add_dependency "parallel_tests", "~> 3.3"
|
21
21
|
|
22
|
-
spec.add_development_dependency "pry", "~> 0.
|
22
|
+
spec.add_development_dependency "pry", "~> 0.14"
|
23
23
|
|
24
24
|
spec.add_runtime_dependency "bundler"
|
25
25
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo_tests
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Zub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.10
|
19
|
+
version: '3.10'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 3.10
|
26
|
+
version: '3.10'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: parallel_tests
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.14'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
54
|
+
version: '0.14'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +74,7 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/snyk_ruby-analysis.yml"
|
77
78
|
- ".github/workflows/tests.yml"
|
78
79
|
- ".gitignore"
|
79
80
|
- ".rspec"
|
@@ -85,12 +86,15 @@ files:
|
|
85
86
|
- README.md
|
86
87
|
- Rakefile
|
87
88
|
- bin/turbo_tests
|
89
|
+
- fixtures/rspec/errors_outside_of_examples_spec.rb
|
90
|
+
- fixtures/rspec/pending_exceptions_spec.rb
|
88
91
|
- lib/turbo_tests.rb
|
89
92
|
- lib/turbo_tests/cli.rb
|
90
93
|
- lib/turbo_tests/json_rows_formatter.rb
|
91
94
|
- lib/turbo_tests/reporter.rb
|
92
95
|
- lib/turbo_tests/runner.rb
|
93
96
|
- lib/turbo_tests/version.rb
|
97
|
+
- lib/utils/hash_extension.rb
|
94
98
|
- turbo_tests.gemspec
|
95
99
|
homepage: https://github.com/serpapi/turbo_tests
|
96
100
|
licenses:
|
@@ -107,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
111
|
requirements:
|
108
112
|
- - ">="
|
109
113
|
- !ruby/object:Gem::Version
|
110
|
-
version: 2.4
|
114
|
+
version: '2.4'
|
111
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
116
|
requirements:
|
113
117
|
- - ">="
|