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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae7178127f711a2ea749e60ff16ed6e3f46011737c02bf1fd2912b6777869b0a
4
- data.tar.gz: d07339025c120451ca4e30fd13da954ded64234081759c0b0e9562954447462a
3
+ metadata.gz: b8a0dc37755b687ea503d265d864eb959d2bda3668e3f21c4b09af48e6fb852a
4
+ data.tar.gz: 8399b27819ae06f123b0695754fe074cb8819d385cd2a4d932c12b9f30a88124
5
5
  SHA512:
6
- metadata.gz: cbb71d599830fa9e86287f389aca4c678794b2258d287dab88bd975f7648bbb4e78b9986c24274b231730d069c88df0d3ce81ce46797723ae44248dec056355f
7
- data.tar.gz: 12df6012daff0f3925aab5204247174d07a6d41d85b6c3a3a79f6f9a79cdc1a7d3c0b4e874cdf405024913d856683539e31257ab4863883f57af77acbda23154
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
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
+ --tty
3
4
  --require spec_helper
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in turbo_tests.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake", "~> 12.0"
6
+ gem "rake", "~> 13.0"
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- turbo_tests (1.2.0)
4
+ turbo_tests (1.2.5)
5
5
  bundler
6
6
  parallel_tests (~> 3.3)
7
- rspec (~> 3.10.0)
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.4.0)
16
+ parallel_tests (3.7.0)
17
17
  parallel
18
- pry (0.13.1)
18
+ pry (0.14.1)
19
19
  coderay (~> 1.1)
20
20
  method_source (~> 1.0)
21
- rake (12.3.3)
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.0)
26
+ rspec-core (3.10.1)
27
27
  rspec-support (~> 3.10.0)
28
- rspec-expectations (3.10.0)
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.0)
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.0)
34
+ rspec-support (3.10.2)
35
35
 
36
36
  PLATFORMS
37
37
  ruby
38
38
 
39
39
  DEPENDENCIES
40
- pry (~> 0.13)
41
- rake (~> 12.0)
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,5 @@
1
+ RSpec.describe "Fixture of spec file with errors outside of examples" do
2
+ it("passes") { expect(2 * 2).to eql(4) }
3
+
4
+ 1 / 0
5
+ end
@@ -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["class_name"]
26
+ obj[:class_name]
27
27
  end
28
28
  }
29
29
 
30
30
  klass.new(
31
- obj["backtrace"],
32
- obj["message"],
33
- FakeException.from_obj(obj["cause"])
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["example_skipped?"],
44
- obj["pending_message"],
45
- obj["status"].to_sym,
46
- obj["pending_fixed?"],
47
- FakeException.from_obj(obj["exception"])
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["metadata"]
56
+ metadata = obj[:metadata]
56
57
 
57
- metadata["shared_group_inclusion_backtrace"].map! do |frame|
58
+ metadata[:shared_group_inclusion_backtrace].map! do |frame|
58
59
  RSpec::Core::SharedExampleGroupInclusionStackFrame.new(
59
- frame["shared_group_name"],
60
- frame["inclusion_location"]
60
+ frame[:shared_group_name],
61
+ frame[:inclusion_location]
61
62
  )
62
63
  end
63
64
 
64
- metadata[:shared_group_inclusion_backtrace] = metadata.delete("shared_group_inclusion_backtrace")
65
+ metadata[:shared_group_inclusion_backtrace] = metadata.delete(:shared_group_inclusion_backtrace)
65
66
 
66
67
  new(
67
- FakeExecutionResult.from_obj(obj["execution_result"]),
68
- obj["location"],
69
- obj["description"],
70
- obj["full_description"],
68
+ FakeExecutionResult.from_obj(obj[:execution_result]),
69
+ obj[:location],
70
+ obj[:description],
71
+ obj[:full_description],
71
72
  metadata,
72
- obj["location_rerun_argument"]
73
+ obj[:location_rerun_argument]
73
74
  )
74
75
  end
75
76
 
@@ -17,7 +17,7 @@ module TurboTests
17
17
  fail_fast = nil
18
18
 
19
19
  OptionParser.new { |opts|
20
- opts.banner = <<-BANNER.gsub(/^ /, "")
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
- "type" => :example_passed,
41
- "example" => example_to_json(notification.example)
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
- "type" => :example_pending,
48
- "example" => example_to_json(notification.example)
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
- "type" => :example_failed,
55
- "example" => example_to_json(notification.example)
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
- "type" => :seed,
62
- "seed" => notification.seed
86
+ type: :seed,
87
+ seed: notification.seed
63
88
  )
64
89
  end
65
90
 
66
91
  def close(notification)
67
92
  output_row(
68
- "type" => :close
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
- "class_name" => exception.class.name.to_s,
78
- "backtrace" => exception.backtrace,
79
- "message" => exception.message,
80
- "cause" => exception_to_json(exception.cause)
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
- "example_skipped?" => result.example_skipped?,
88
- "pending_message" => result.pending_message,
89
- "status" => result.status,
90
- "pending_fixed?" => result.pending_fixed?,
91
- "exception" => exception_to_json(result.exception)
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
- "shared_group_name" => frame.shared_group_name,
98
- "inclusion_location" => frame.inclusion_location
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
- "execution_result" => execution_result_to_json(example.execution_result),
105
- "location" => example.location,
106
- "description" => example.description,
107
- "full_description" => example.full_description,
108
- "metadata" => {
109
- "shared_group_inclusion_backtrace" =>
110
- example.metadata[:shared_group_inclusion_backtrace].map { |frame| stack_frame_to_json(frame) }
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
- "location_rerun_argument" => example.location_rerun_argument
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 ENV["RSPEC_FORMATTER_OUTPUT_ID"] + obj.to_json
166
+ output.puts(obj.to_json)
167
+ output.flush
118
168
  end
119
169
  end
120
170
  end
@@ -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 == "-" ? STDOUT : File.open(filename, "w")
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
- end_time = Time.now
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
- 0,
88
- 0
110
+ @load_time,
111
+ @errors_outside_of_examples_count
89
112
  ))
90
113
  delegate_to_formatters(:close,
91
114
  RSpec::Core::Notifications::NullNotification)
@@ -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
- start_time = opts.fetch(:start_time) { Time.now }
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
- runtime_log: @runtime_log
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.each_with_index do |tests, process_id|
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
- protected
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
- def start_regular_subprocess(tests, process_id)
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
- "type" => "exit",
87
- "process_id" => process_id
123
+ type: "exit",
124
+ process_id: process_id
88
125
  }
89
126
  else
90
- require "securerandom"
91
- env["RSPEC_FORMATTER_OUTPUT_ID"] = SecureRandom.uuid
92
- env["RUBYOPT"] = "-I#{File.expand_path("..", __dir__)}"
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(2**16).to_s,
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
- _stdin, stdout, stderr, _wait_thr = Open3.popen3(env, *command)
166
+ stdin, stdout, stderr, wait_thr = Open3.popen3(env, *command)
167
+ stdin.close
114
168
 
115
169
  @threads <<
116
- Thread.new {
117
- require "json"
118
- stdout.each_line do |line|
119
- result = line.split(env["RSPEC_FORMATTER_OUTPUT_ID"])
120
-
121
- output = result.shift
122
- STDOUT.print(output) unless output.empty?
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 << {"type" => "exit", "process_id" => process_id}
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["type"]
214
+ case message[:type]
157
215
  when "example_passed"
158
- example = FakeExample.from_obj(message["example"])
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["example"])
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["example"])
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
- warn("Unhandled message in main process: #{message}")
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
@@ -1,3 +1,3 @@
1
1
  module TurboTests
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.5"
3
3
  end
@@ -0,0 +1,9 @@
1
+ module CoreExtensions
2
+ refine Hash do
3
+ def to_struct
4
+ OpenStruct.new(self.each_with_object({}) do |(key, val), acc|
5
+ acc[key] = val.is_a?(Hash) ? val.to_struct : val
6
+ end)
7
+ end
8
+ end
9
+ end
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.0"
17
+ spec.required_ruby_version = ">= 2.4"
18
18
 
19
- spec.add_dependency "rspec", "~> 3.10.0"
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.13"
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.0
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: 2020-11-23 00:00:00.000000000 Z
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.0
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.0
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.13'
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.13'
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.0
114
+ version: '2.4'
111
115
  required_rubygems_version: !ruby/object:Gem::Requirement
112
116
  requirements:
113
117
  - - ">="