cukeforker-webdriver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .bundle
2
+ .idea
3
+ spec/output
4
+ output
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cukeforker-webdriver (0.0.1)
5
+ capybara
6
+ childprocess
7
+ cucumber
8
+ gherkin
9
+ rspec
10
+ selenium-webdriver
11
+
12
+ GEM
13
+ remote: http://rubygems.org/
14
+ specs:
15
+ builder (3.0.0)
16
+ capybara (1.1.1)
17
+ mime-types (>= 1.16)
18
+ nokogiri (>= 1.3.3)
19
+ rack (>= 1.0.0)
20
+ rack-test (>= 0.5.4)
21
+ selenium-webdriver (~> 2.0)
22
+ xpath (~> 0.1.4)
23
+ childprocess (0.2.2)
24
+ ffi (~> 1.0.6)
25
+ cucumber (1.0.6)
26
+ builder (>= 2.1.2)
27
+ diff-lcs (>= 1.1.2)
28
+ gherkin (~> 2.4.18)
29
+ json (>= 1.4.6)
30
+ term-ansicolor (>= 1.0.6)
31
+ diff-lcs (1.1.3)
32
+ ffi (1.0.9)
33
+ gherkin (2.4.21)
34
+ json (>= 1.4.6)
35
+ json (1.5.4)
36
+ json_pure (1.6.1)
37
+ mime-types (1.16)
38
+ nokogiri (1.5.0)
39
+ rack (1.3.3)
40
+ rack-test (0.6.1)
41
+ rack (>= 1.0)
42
+ rake (0.9.2)
43
+ rspec (2.6.0)
44
+ rspec-core (~> 2.6.0)
45
+ rspec-expectations (~> 2.6.0)
46
+ rspec-mocks (~> 2.6.0)
47
+ rspec-core (2.6.4)
48
+ rspec-expectations (2.6.0)
49
+ diff-lcs (~> 1.1.2)
50
+ rspec-mocks (2.6.0)
51
+ rubyzip (0.9.4)
52
+ selenium-webdriver (2.6.0)
53
+ childprocess (>= 0.2.1)
54
+ ffi (>= 1.0.7)
55
+ json_pure
56
+ rubyzip
57
+ term-ansicolor (1.0.6)
58
+ xpath (0.1.4)
59
+ nokogiri (~> 1.3)
60
+
61
+ PLATFORMS
62
+ ruby
63
+
64
+ DEPENDENCIES
65
+ cukeforker-webdriver!
66
+ rake (>= 0.9.2)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'cukeforker-webdriver'
6
+ s.version = '0.0.1'
7
+ s.authors = ["Derek Ekins"]
8
+ s.description = 'Run webdriver with ports pre allocated - thanks go to Jari Bakken for the original implementation https://gist.github.com/949241'
9
+ s.summary = "#{s.name}-#{s.version}"
10
+ s.email = 'derek@spathi.com'
11
+ s.homepage = "http://github.com/dereke/cukeforker-webdriver"
12
+
13
+ s.platform = Gem::Platform::RUBY
14
+ s.post_install_message = %{
15
+ (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
16
+
17
+ Thank you for installing Cukeforker Web Driver extensions
18
+
19
+ (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
20
+
21
+ }
22
+
23
+ s.add_dependency 'rspec'
24
+ s.add_dependency 'cucumber'
25
+ s.add_dependency 'gherkin'
26
+ s.add_dependency 'capybara'
27
+ s.add_dependency 'selenium-webdriver'
28
+ s.add_dependency 'childprocess'
29
+
30
+ s.add_development_dependency 'rake', '>= 0.9.2'
31
+
32
+ s.rubygems_version = ">= 1.6.1"
33
+ s.files = `git ls-files`.split("\n")
34
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
35
+ s.require_path = "lib"
36
+ end
@@ -0,0 +1,4 @@
1
+ require 'cukeforker-webdriver/port_pool'
2
+ require 'cukeforker-webdriver/parallel_port_manager'
3
+ require 'cukeforker-webdriver/runner'
4
+
@@ -0,0 +1,26 @@
1
+ require 'capybara'
2
+ require_relative '../no_lock_firefox_bridge'
3
+
4
+ class Capybara::Selenium::Driver
5
+ def browser
6
+ unless @browser
7
+ @port = ENV['web_driver_port']
8
+ options[:port] = @port
9
+ @browser = Selenium::WebDriver::Driver.new(CukeForker::WebDriver::NoLockFirefoxBridge.new(options.reject { |key,val| SPECIAL_OPTIONS.include?(key)}))
10
+
11
+ main = Process.pid
12
+ at_exit do
13
+ quit if Process.pid == main
14
+ end
15
+ end
16
+ @browser
17
+ end
18
+
19
+ def quit
20
+ begin
21
+ @browser.quit
22
+ rescue Errno::ECONNREFUSED
23
+ ensure
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ module CukeForker
2
+ module WebDriver
3
+ class NoLockFirefoxBridge < Selenium::WebDriver::Firefox::Bridge
4
+ def initialize(*args)
5
+ super(*args)
6
+ end
7
+
8
+ class FakeLock
9
+ def initialize(*args)
10
+ end
11
+
12
+ def locked
13
+ yield
14
+ end
15
+ end
16
+
17
+ class NoLockLauncher < Selenium::WebDriver::Firefox::Launcher
18
+ private
19
+
20
+ def find_free_port
21
+ end
22
+
23
+ def socket_lock
24
+ @socket_lock ||= FakeLock.new
25
+ end
26
+ end
27
+
28
+ def create_launcher(port, profile)
29
+ NoLockLauncher.new(Selenium::WebDriver::Firefox::Binary.new, port, profile)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ require 'cukeforker/abstract_listener'
2
+
3
+ module CukeForker
4
+ module WebDriver
5
+ class ParallelPortManager < CukeForker::AbstractListener
6
+ def initialize(max)
7
+ @port_pool = CukeForker::WebDriver::PortPool.new max
8
+ @available_ports = []
9
+ until @available_ports.length == max
10
+ @available_ports << @port_pool.get
11
+ end
12
+ end
13
+
14
+ def on_worker_starting(worker)
15
+ worker.data.web_driver_port = @available_ports.shift
16
+ end
17
+
18
+ def on_worker_forked(worker)
19
+ require_relative 'capybara/driver'
20
+ ENV['web_driver_port'] = worker.data.web_driver_port.to_s
21
+ end
22
+
23
+ def on_worker_finished(worker)
24
+ @available_ports << worker.data.web_driver_port
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,150 @@
1
+ require 'socket'
2
+ require 'childprocess'
3
+
4
+ module CukeForker
5
+ module WebDriver
6
+ class PortPool
7
+
8
+ class TooManyPortsError < StandardError; end
9
+ class TimeoutError < StandardError; end
10
+ class BindError < StandardError; end
11
+
12
+ START_PORT = 1024
13
+ HOST = "127.0.0.1"
14
+ RELEASE_TIMEOUT = 20
15
+ PORT_INCREMENT = 2
16
+
17
+ def initialize(capacity)
18
+ @last_port = START_PORT - 1
19
+ @capacity = capacity
20
+ @servers = []
21
+ @running = []
22
+
23
+ bind
24
+ end
25
+
26
+ def get
27
+ if @servers.empty?
28
+ STDERR.puts "out of ports, finding next"
29
+ @servers << next_server
30
+ end
31
+
32
+ s = @servers.shift
33
+ port = s.addr.fetch(1)
34
+ s.close
35
+
36
+ wait_while_listening port
37
+ port
38
+ end
39
+
40
+ def release(port)
41
+ raise TooManyPortsError if @servers.size == @capacity
42
+ server = wait_for_server(port)
43
+ @servers << server
44
+ rescue WebDriverPortPool::TimeoutError
45
+ STDERR.puts "timed out while releasing #{port}"
46
+ end
47
+
48
+ def stop
49
+ @servers.each { |s|
50
+ begin
51
+ s.close
52
+ rescue IOError
53
+ end
54
+ }
55
+ end
56
+
57
+ def close_on_exec
58
+ STDERR.puts "#{self.class}: #{Process.pid} pool closed on exec"
59
+ @servers.each { |s| ChildProcess.close_on_exec s }
60
+ end
61
+
62
+ def size
63
+ @servers.size
64
+ end
65
+
66
+ private
67
+
68
+ def bind
69
+ @servers << next_server until size == @capacity
70
+ end
71
+
72
+ def next_server
73
+ max_ports = @capacity*100
74
+ upper_bound = START_PORT + max_ports
75
+ @last_port += PORT_INCREMENT
76
+
77
+ until server = try_bind(@last_port)
78
+ if @last_port > upper_bound
79
+ raise BindError, "unable to find free port within #{START_PORT}..#{upper_bound} tries, last port tried #{@last_port}"
80
+ end
81
+
82
+ @last_port += PORT_INCREMENT
83
+ end
84
+
85
+ server
86
+ end
87
+
88
+ def try_bind(port)
89
+ server_for port
90
+ rescue SocketError, Errno::EADDRINUSE
91
+ # ok
92
+ end
93
+
94
+ def server_for(port)
95
+ s = TCPServer.new(HOST, port)
96
+ ChildProcess.close_on_exec s # make sure browsers don't inherit the file descriptors
97
+ s
98
+ end
99
+
100
+ def wait_for_server(port)
101
+ wait_until(RELEASE_TIMEOUT) {
102
+ res = try_bind port
103
+ # p `lsof -i TCP:#{port}` unless res
104
+
105
+ res
106
+ }
107
+ end
108
+
109
+ def wait_while_listening(port)
110
+ wait_until(RELEASE_TIMEOUT) {
111
+ not listening?(port, 5)
112
+ }
113
+ end
114
+
115
+ def wait_until(timeout, &blk)
116
+ max_time = Time.now + timeout
117
+
118
+ until res = yield
119
+ if Time.now >= max_time
120
+ raise TimeoutError, "timed out"
121
+ else
122
+ sleep 0.1
123
+ end
124
+ end
125
+
126
+ res
127
+ end
128
+
129
+ def listening?(port, timeout = nil)
130
+ addr = Socket.getaddrinfo(HOST, nil)
131
+ sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
132
+
133
+ if timeout
134
+ secs = Integer(timeout)
135
+ usecs = Integer((timeout - secs) * 1_000_000)
136
+ optval = [secs, usecs].pack("l_2")
137
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
138
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
139
+ end
140
+ sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
141
+ sock.close
142
+
143
+ true
144
+ rescue => e
145
+ false
146
+ end
147
+
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,15 @@
1
+ module CukeForker
2
+ module WebDriver
3
+ class Runner
4
+ def self.run(features, options)
5
+ max = options.fetch(:max, 2)
6
+ options[:notify] = CukeForker::WebDriver::ParallelPortManager.new(max)
7
+
8
+ CukeForker::Runner.run(
9
+ features,
10
+ options
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cukeforker-webdriver
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Derek Ekins
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-21 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: cucumber
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: gherkin
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: capybara
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :runtime
76
+ version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: selenium-webdriver
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ type: :runtime
90
+ version_requirements: *id005
91
+ - !ruby/object:Gem::Dependency
92
+ name: childprocess
93
+ prerelease: false
94
+ requirement: &id006 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ type: :runtime
104
+ version_requirements: *id006
105
+ - !ruby/object:Gem::Dependency
106
+ name: rake
107
+ prerelease: false
108
+ requirement: &id007 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 63
114
+ segments:
115
+ - 0
116
+ - 9
117
+ - 2
118
+ version: 0.9.2
119
+ type: :development
120
+ version_requirements: *id007
121
+ description: Run webdriver with ports pre allocated - thanks go to Jari Bakken for the original implementation https://gist.github.com/949241
122
+ email: derek@spathi.com
123
+ executables: []
124
+
125
+ extensions: []
126
+
127
+ extra_rdoc_files: []
128
+
129
+ files:
130
+ - .gitignore
131
+ - Gemfile
132
+ - Gemfile.lock
133
+ - Rakefile
134
+ - cukeforker-webdriver.gemspec
135
+ - lib/cukeforker-webdriver.rb
136
+ - lib/cukeforker-webdriver/capybara/driver.rb
137
+ - lib/cukeforker-webdriver/no_lock_firefox_bridge.rb
138
+ - lib/cukeforker-webdriver/parallel_port_manager.rb
139
+ - lib/cukeforker-webdriver/port_pool.rb
140
+ - lib/cukeforker-webdriver/runner.rb
141
+ has_rdoc: true
142
+ homepage: http://github.com/dereke/cukeforker-webdriver
143
+ licenses: []
144
+
145
+ post_install_message: |+
146
+
147
+ (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
148
+
149
+ Thank you for installing Cukeforker Web Driver extensions
150
+
151
+ (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
152
+
153
+ rdoc_options: []
154
+
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ hash: 3
163
+ segments:
164
+ - 0
165
+ version: "0"
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ hash: 3
172
+ segments:
173
+ - 0
174
+ version: "0"
175
+ requirements: []
176
+
177
+ rubyforge_project:
178
+ rubygems_version: 1.6.2
179
+ signing_key:
180
+ specification_version: 3
181
+ summary: cukeforker-webdriver-0.0.1
182
+ test_files: []
183
+