turbo_tests 1.2.0 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Tests](https://github.com/serpapi/turbo_tests/workflows/Tests/badge.svg)
|
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
|
- - ">="
|