cucumber-pro 0.0.4 → 0.0.6
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/.rspec +1 -0
- data/Rakefile +5 -1
- data/TODO.md +1 -5
- data/lib/cucumber/pro.rb +7 -4
- data/lib/cucumber/pro/formatter.rb +2 -1
- data/lib/cucumber/pro/scm.rb +12 -5
- data/lib/cucumber/pro/version +1 -1
- data/lib/cucumber/pro/web_socket/session.rb +139 -0
- data/spec/cucumber/pro/scm/git_repo_spec.rb +40 -0
- data/spec/cucumber/pro/web_socket/worker_spec.rb +79 -0
- metadata +8 -3
- data/lib/cucumber/pro/web_socket_session.rb +0 -123
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 130928ede57f9a40d1b22d0e66f3ed254d614aeb
|
4
|
+
data.tar.gz: 00dd538d850dee73f4d8a2caf58858845942274c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52da99dc9bfd36bf1369d4f0e4069258bdfc4accde72781f6b63b555d2a239faa4472567eec372c5ece35a8ffd844e9705b513aee222734da7f6a9436c942687
|
7
|
+
data.tar.gz: 76a252ee7416ea06cac3c42743e9ec8b848b2952c788cd85ee0e804739813e887eca56905e2c06b336c19e1809dfff5bd5bd15043ddf9e0d73fcfcea5efb2465
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --order random
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'bundler'
|
|
3
3
|
Bundler::GemHelper.install_tasks
|
4
4
|
|
5
5
|
desc 'Run tests'
|
6
|
-
task default: :cucumber
|
6
|
+
task default: [:rspec, :cucumber]
|
7
7
|
|
8
8
|
ENV['cucumber_pro_log_path'] = File.dirname(__FILE__) + '/tmp/test.log'
|
9
9
|
|
@@ -11,6 +11,10 @@ task :cucumber do
|
|
11
11
|
sh 'cucumber'
|
12
12
|
end
|
13
13
|
|
14
|
+
task :rspec do
|
15
|
+
sh 'rspec'
|
16
|
+
end
|
17
|
+
|
14
18
|
desc 'Run repeated tests to check for async bugs'
|
15
19
|
task :soak, :repetitions do |task, args|
|
16
20
|
reps = args[:repetitions] || 10
|
data/TODO.md
CHANGED
@@ -1,9 +1,5 @@
|
|
1
|
-
- Scenario for when the token is invalid
|
2
|
-
- Scenario for when branch is invalid
|
3
|
-
- Branch has a * in it
|
4
|
-
- Better (more unique) run ID - use a UUID
|
5
|
-
- Log to Cucumber's IO instead of STDOUT by default
|
6
1
|
- Unit tests for Messaging
|
7
2
|
- Handle cases where some messages sent are not ack'd
|
8
3
|
- Handle cases where some messages sent come back with an error
|
4
|
+
- Allow configuration of remote
|
9
5
|
|
data/lib/cucumber/pro.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'cucumber/pro/formatter'
|
3
|
-
require 'cucumber/pro/
|
3
|
+
require 'cucumber/pro/web_socket/session'
|
4
4
|
require 'cucumber/pro/version'
|
5
5
|
|
6
6
|
module Cucumber
|
7
7
|
module Pro
|
8
8
|
|
9
9
|
class << self
|
10
|
-
def new(
|
11
|
-
session =
|
10
|
+
def new(runtime, output, options)
|
11
|
+
session = WebSocket::Session.new(url, logger(output))
|
12
12
|
Formatter.new(session)
|
13
13
|
end
|
14
14
|
|
@@ -26,6 +26,10 @@ module Cucumber
|
|
26
26
|
token = config.token || raise(Error::MissingToken.new)
|
27
27
|
config.url + "?token=#{token}"
|
28
28
|
end
|
29
|
+
|
30
|
+
def logger(output)
|
31
|
+
config.logger || Logger.new(output)
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
class Config
|
@@ -35,7 +39,6 @@ module Cucumber
|
|
35
39
|
# Default config
|
36
40
|
configure do |config|
|
37
41
|
config.url = 'wss://results.cucumber.pro/ws'
|
38
|
-
config.logger = Logger.new(ENV['cucumber_pro_log_path'] || STDOUT)
|
39
42
|
config.token = ENV['CUCUMBER_PRO_TOKEN']
|
40
43
|
end
|
41
44
|
|
data/lib/cucumber/pro/scm.rb
CHANGED
@@ -9,33 +9,40 @@ module Cucumber
|
|
9
9
|
|
10
10
|
def self.find(path = Dir.pwd)
|
11
11
|
if Dir.entries(path).include? '.git'
|
12
|
-
new(path)
|
12
|
+
GitRepo.new(path)
|
13
13
|
else
|
14
14
|
raise NoGitRepoFound if path == '/'
|
15
15
|
find File.expand_path(path + '/..')
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
end
|
20
|
+
|
21
|
+
class GitRepo
|
22
|
+
|
19
23
|
def initialize(path)
|
20
24
|
@path = path
|
21
25
|
end
|
22
26
|
|
23
27
|
def remote
|
24
|
-
cmd('git config --get remote.origin.url')
|
28
|
+
cmd('git config --get remote.origin.url').last
|
25
29
|
end
|
26
30
|
|
27
31
|
def branch
|
28
|
-
cmd("git branch --contains #{rev}")
|
32
|
+
branch = cmd("git branch --contains #{rev}").
|
33
|
+
reject { |b| /^\* \(detached from \w+\)/.match b }.
|
34
|
+
first.
|
35
|
+
gsub(/^\* /, '')
|
29
36
|
end
|
30
37
|
|
31
38
|
def rev
|
32
|
-
cmd("git rev-parse HEAD")
|
39
|
+
cmd("git rev-parse HEAD").last
|
33
40
|
end
|
34
41
|
|
35
42
|
private
|
36
43
|
|
37
44
|
def cmd(cmd)
|
38
|
-
Dir.chdir(@path) { `#{cmd}` }.strip
|
45
|
+
Dir.chdir(@path) { `#{cmd}` }.lines.map &:strip
|
39
46
|
end
|
40
47
|
end
|
41
48
|
end
|
data/lib/cucumber/pro/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'faye/websocket'
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
module Cucumber
|
6
|
+
module Pro
|
7
|
+
module WebSocket
|
8
|
+
|
9
|
+
class Session
|
10
|
+
|
11
|
+
def initialize(url, logger)
|
12
|
+
@url, @logger = url, logger
|
13
|
+
create_socket = -> worker {
|
14
|
+
ws = Faye::WebSocket::Client.new(@url)
|
15
|
+
ws.on :open, &worker.method(:on_open)
|
16
|
+
ws.on :error, &worker.method(:on_error)
|
17
|
+
ws.on :message, &worker.method(:on_message)
|
18
|
+
ws.on :close, &worker.method(:on_close)
|
19
|
+
ws
|
20
|
+
}
|
21
|
+
@queue = Queue.new
|
22
|
+
@socket = Worker.new(create_socket, logger, self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def send(message)
|
26
|
+
logger.debug [:session, :send, message]
|
27
|
+
socket.send(message.to_json)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
logger.debug [:session, :close]
|
33
|
+
socket.close
|
34
|
+
loop until socket.closed?
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def error(exception)
|
39
|
+
logger.fatal exception
|
40
|
+
$stderr.puts "Cucumber Pro failed to send results: #{exception}"
|
41
|
+
$stderr.puts exception.backtrace.join("\n")
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :logger, :queue, :socket
|
48
|
+
end
|
49
|
+
|
50
|
+
class Worker
|
51
|
+
|
52
|
+
def initialize(create_socket, logger, error_handler)
|
53
|
+
@create_socket, @logger, @error_handler = create_socket, logger, error_handler
|
54
|
+
@q = Queue.new
|
55
|
+
@em = Thread.new { start_client }
|
56
|
+
@ack_count = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def close
|
60
|
+
@q << -> {
|
61
|
+
if @ack_count == 0
|
62
|
+
@ws.close
|
63
|
+
else
|
64
|
+
EM.next_tick { close }
|
65
|
+
end
|
66
|
+
}
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def send(data)
|
71
|
+
@q << -> {
|
72
|
+
@ws.send data
|
73
|
+
@ack_count += 1
|
74
|
+
}
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def closed?
|
79
|
+
!@em.alive?
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
attr_reader :logger, :error_handler, :next_task
|
85
|
+
|
86
|
+
def start_client
|
87
|
+
EM.run do
|
88
|
+
logger.debug [:ws, :start]
|
89
|
+
@ws = @create_socket.call(self)
|
90
|
+
end
|
91
|
+
self
|
92
|
+
rescue => exception
|
93
|
+
error_handler.error exception
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_open(event)
|
97
|
+
logger.debug [:ws, :open]
|
98
|
+
process_tasks
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_error(event)
|
103
|
+
logger.error [:ws, :error]
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_message(event)
|
108
|
+
logger.debug [:ws, :message, event.data]
|
109
|
+
@ack_count -= 1
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def on_close(event)
|
114
|
+
logger.debug [:ws, :close]
|
115
|
+
if access_denied?(event)
|
116
|
+
raise Error::AccessDenied.new
|
117
|
+
end
|
118
|
+
@ws = nil
|
119
|
+
EM.stop_event_loop
|
120
|
+
self
|
121
|
+
end
|
122
|
+
|
123
|
+
def process_tasks
|
124
|
+
@q.pop.call if !@q.empty?
|
125
|
+
EM.next_tick { process_tasks }
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
def access_denied?(event)
|
130
|
+
event.code == 1002 &&
|
131
|
+
event.reason == \
|
132
|
+
"Error during WebSocket handshake: Unexpected response code: 401"
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'cucumber/pro/scm'
|
2
|
+
require 'aruba/api'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Pro
|
6
|
+
module Scm
|
7
|
+
describe GitRepo do
|
8
|
+
include Aruba::Api
|
9
|
+
before do
|
10
|
+
clean_current_dir
|
11
|
+
end
|
12
|
+
|
13
|
+
it "figures out the name of the branch, even on CI" do
|
14
|
+
in_current_dir do
|
15
|
+
run_simple "git init"
|
16
|
+
run_simple "git config user.email \"test@test.com\""
|
17
|
+
run_simple "git config user.name \"Test user\""
|
18
|
+
run_simple "git commit --allow-empty -m 'Initial commit'"
|
19
|
+
run_simple "git rev-parse HEAD"
|
20
|
+
rev = all_stdout.split("\n").last
|
21
|
+
run_simple "git checkout #{rev}"
|
22
|
+
repo = Repo.find(current_dir)
|
23
|
+
expect( repo.branch ).to eq "master"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "figures out the name of the branch when that's what's checked out" do
|
28
|
+
in_current_dir do
|
29
|
+
run_simple "git init"
|
30
|
+
run_simple "git config user.email \"test@test.com\""
|
31
|
+
run_simple "git config user.name \"Test user\""
|
32
|
+
run_simple "git commit --allow-empty -m 'Initial commit'"
|
33
|
+
repo = Repo.find(current_dir)
|
34
|
+
expect( repo.branch ).to eq "master"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'cucumber/pro/web_socket/session'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Cucumber::Pro::WebSocket
|
5
|
+
|
6
|
+
describe Worker do
|
7
|
+
let(:good_data) { double('good data') }
|
8
|
+
let(:logger) { Logger.new(STDOUT) }
|
9
|
+
let(:socket) { FakeSocket.new }
|
10
|
+
let(:worker) { Worker.new(self.method(:create_fake_socket), logger, logger) }
|
11
|
+
|
12
|
+
before { logger.level = Logger::DEBUG }
|
13
|
+
|
14
|
+
it "closes once all messages have been acknowledged (but not before)" do
|
15
|
+
worker.send(good_data)
|
16
|
+
worker.close
|
17
|
+
eventually do
|
18
|
+
socket.data.last.should == good_data
|
19
|
+
end
|
20
|
+
eventually do
|
21
|
+
expect( worker ).to_not be_closed
|
22
|
+
end
|
23
|
+
socket.send_ack
|
24
|
+
eventually do
|
25
|
+
expect( worker ).to be_closed
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_fake_socket(worker)
|
30
|
+
socket.worker = worker
|
31
|
+
EM.next_tick {
|
32
|
+
worker.method(:on_open).call(double('ws event'))
|
33
|
+
}
|
34
|
+
socket
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class FakeSocket
|
39
|
+
include RSpec::Mocks::ExampleMethods
|
40
|
+
|
41
|
+
attr_accessor :worker
|
42
|
+
attr_reader :data
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
@data = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def close
|
49
|
+
worker.method(:on_close).call(ws_event(1000))
|
50
|
+
end
|
51
|
+
|
52
|
+
def send(data)
|
53
|
+
@data << data
|
54
|
+
end
|
55
|
+
|
56
|
+
def send_ack
|
57
|
+
event = ws_event(1000, { 'type' => 'metadata_saved' })
|
58
|
+
worker.method(:on_message).call(event)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def ws_event(code, data = {})
|
64
|
+
double('ws event', code: 1000, data: data)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
require 'anticipate'
|
69
|
+
include Anticipate
|
70
|
+
def eventually(&block)
|
71
|
+
result = nil
|
72
|
+
sleeping(0.1).seconds.between_tries.failing_after(30).tries do
|
73
|
+
result = block.call
|
74
|
+
end
|
75
|
+
result
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cucumber-pro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Wynne
|
@@ -142,6 +142,7 @@ executables: []
|
|
142
142
|
extensions: []
|
143
143
|
extra_rdoc_files: []
|
144
144
|
files:
|
145
|
+
- ".rspec"
|
145
146
|
- Gemfile
|
146
147
|
- LICENSE
|
147
148
|
- README.md
|
@@ -160,7 +161,9 @@ files:
|
|
160
161
|
- lib/cucumber/pro/scm.rb
|
161
162
|
- lib/cucumber/pro/version
|
162
163
|
- lib/cucumber/pro/version.rb
|
163
|
-
- lib/cucumber/pro/
|
164
|
+
- lib/cucumber/pro/web_socket/session.rb
|
165
|
+
- spec/cucumber/pro/scm/git_repo_spec.rb
|
166
|
+
- spec/cucumber/pro/web_socket/worker_spec.rb
|
164
167
|
- tmp/.gitkeep
|
165
168
|
homepage: https://cucumber.pro
|
166
169
|
licenses:
|
@@ -186,7 +189,7 @@ rubyforge_project:
|
|
186
189
|
rubygems_version: 2.0.14
|
187
190
|
signing_key:
|
188
191
|
specification_version: 4
|
189
|
-
summary: cucumber-pro-0.0.
|
192
|
+
summary: cucumber-pro-0.0.6
|
190
193
|
test_files:
|
191
194
|
- features/publish_results.feature
|
192
195
|
- features/security.feature
|
@@ -195,4 +198,6 @@ test_files:
|
|
195
198
|
- features/support/env.rb
|
196
199
|
- features/support/fake_results_service.rb
|
197
200
|
- features/support/world.rb
|
201
|
+
- spec/cucumber/pro/scm/git_repo_spec.rb
|
202
|
+
- spec/cucumber/pro/web_socket/worker_spec.rb
|
198
203
|
has_rdoc:
|
@@ -1,123 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'faye/websocket'
|
3
|
-
require 'eventmachine'
|
4
|
-
|
5
|
-
module Cucumber
|
6
|
-
module Pro
|
7
|
-
class WebSocketSession
|
8
|
-
|
9
|
-
def initialize(url, logger)
|
10
|
-
@url, @logger = url, logger
|
11
|
-
@queue = Queue.new
|
12
|
-
@socket = SocketWorker.new(url, logger, self) do
|
13
|
-
queue.pop.call if !queue.empty?
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def send(message)
|
18
|
-
logger.debug [:session, :send, message]
|
19
|
-
queue.push -> { socket.send(message.to_json) }
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def close
|
24
|
-
logger.debug [:session, :close]
|
25
|
-
queue.push -> { socket.close }
|
26
|
-
loop until socket.closed?
|
27
|
-
self
|
28
|
-
end
|
29
|
-
|
30
|
-
def error(exception)
|
31
|
-
logger.fatal exception
|
32
|
-
$stderr.puts "Cucumber Pro failed to send results: #{exception}"
|
33
|
-
$stderr.puts exception.backtrace.join("\n")
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
attr_reader :logger, :queue, :socket
|
40
|
-
|
41
|
-
class SocketWorker
|
42
|
-
|
43
|
-
def initialize(url, logger, error_handler, &next_task)
|
44
|
-
@url, @logger, @error_handler = url, logger, error_handler
|
45
|
-
@next_task = next_task
|
46
|
-
@em = Thread.new { start_client }
|
47
|
-
end
|
48
|
-
|
49
|
-
def close
|
50
|
-
@ws.close
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
|
-
def send(data)
|
55
|
-
@ws.send data
|
56
|
-
self
|
57
|
-
end
|
58
|
-
|
59
|
-
def closed?
|
60
|
-
!@em.alive?
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
attr_reader :logger, :error_handler, :next_task
|
66
|
-
|
67
|
-
def start_client
|
68
|
-
EM.run do
|
69
|
-
logger.debug [:ws, :start]
|
70
|
-
@ws = Faye::WebSocket::Client.new(@url)
|
71
|
-
@ws.on :open, &self.method(:on_open)
|
72
|
-
@ws.on :error, &self.method(:on_error)
|
73
|
-
@ws.on :message, &self.method(:on_message)
|
74
|
-
@ws.on :close, &self.method(:on_close)
|
75
|
-
end
|
76
|
-
self
|
77
|
-
rescue => exception
|
78
|
-
error_handler.error exception
|
79
|
-
end
|
80
|
-
|
81
|
-
def on_open(event)
|
82
|
-
logger.debug [:ws, :open]
|
83
|
-
process_tasks
|
84
|
-
self
|
85
|
-
end
|
86
|
-
|
87
|
-
def on_error(event)
|
88
|
-
logger.error [:ws, :error]
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_message(event)
|
93
|
-
logger.debug [:ws, :message, event.data]
|
94
|
-
self
|
95
|
-
end
|
96
|
-
|
97
|
-
def on_close(event)
|
98
|
-
logger.debug [:ws, :close]
|
99
|
-
if access_denied?(event)
|
100
|
-
raise Error::AccessDenied.new
|
101
|
-
end
|
102
|
-
@ws = nil
|
103
|
-
EM.stop_event_loop
|
104
|
-
self
|
105
|
-
end
|
106
|
-
|
107
|
-
def process_tasks
|
108
|
-
next_task.call
|
109
|
-
EM.next_tick { process_tasks }
|
110
|
-
self
|
111
|
-
end
|
112
|
-
|
113
|
-
def access_denied?(event)
|
114
|
-
event.code == 1002 &&
|
115
|
-
event.reason == \
|
116
|
-
"Error during WebSocket handshake: Unexpected response code: 401"
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|