blade-sauce_labs_plugin 0.4.1 → 0.5.0

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
  SHA1:
3
- metadata.gz: 397a14f391a37332e45ae9dd8a3e8f0d131d3b13
4
- data.tar.gz: d1c7d0c7354eb14d946d79324fae80d627fe404b
3
+ metadata.gz: ff2fb3a13a5496da6770ba9bb14ecd45fcb419e7
4
+ data.tar.gz: 9670b7048dda5d53f37c8bbb11192cd49cd09d23
5
5
  SHA512:
6
- metadata.gz: 3947a1c89e2a0ef6a2630c1da9ded0487f2b3af9e30ab02d253331ab4ee8c3f3ef2ae1ba8bc87887caad932c299b56654bb7c172f916c510f78fc618c2c2423d
7
- data.tar.gz: 96409e51d49c3afc38f9cebd6c9afacedd7bfbcb4ee8cbef0ed78a620d6bc72adbb10114180ef6c139ed00cf658e015f0889e2eaaa7719f229ad3b3579e2b81c
6
+ metadata.gz: 85c344a1488575ba237517c51ce6128762d905730b6feb2817ec54d06975a535ca95bbceac35ea44bb6da024ec64978b1805148c7282014e1728a610f69f1551
7
+ data.tar.gz: ce34f8d0aaace9c6e2acbdb62d5b59396cc1eb973356dc8701073498d8fba49513a1c52f7cd8e198d8978e110b3be06713794ea5576ec3ab172730072a4ba9ca
@@ -19,10 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.10"
22
+ spec.add_development_dependency "blade", "~> 0.5.0"
22
23
  spec.add_development_dependency "rake", "~> 10.0"
23
24
  spec.add_development_dependency "webmock", "~> 1.21.0"
24
25
 
25
- spec.add_dependency "blade"
26
+ spec.add_dependency "selenium-webdriver"
26
27
  spec.add_dependency "faraday"
27
28
  spec.add_dependency "childprocess"
28
29
  end
@@ -9,8 +9,9 @@ module Blade::SauceLabsPlugin::Client
9
9
  "IE" => "Internet Explorer"
10
10
  }
11
11
 
12
- extend Forwardable
13
- def_delegators Blade::SauceLabsPlugin, :config, :username, :access_key, :debug?
12
+ MOBILE_PATTERN = /iphone|ipad|android/i
13
+
14
+ delegate :config, :username, :access_key, :debug?, to: Blade::SauceLabsPlugin
14
15
 
15
16
  def request(method, path, params = {})
16
17
  connection.send(method) do |req|
@@ -20,6 +21,27 @@ module Blade::SauceLabsPlugin::Client
20
21
  end
21
22
  end
22
23
 
24
+ def get_jobs(options = {})
25
+ JSON.parse(request(:get, "/rest/v1/#{username}/jobs?#{options.to_query}").body)
26
+ end
27
+
28
+ def update_job(id, params = {})
29
+ request(:put, "/rest/v1/#{username}/jobs/#{id}", params)
30
+ end
31
+
32
+ def stop_job(id)
33
+ request(:put, "/rest/v1/#{username}/jobs/#{id}/stop")
34
+ end
35
+
36
+ def delete_job(id)
37
+ request(:delete, "/rest/v1/#{username}/jobs/#{id}")
38
+ end
39
+
40
+ def get_available_vm_count
41
+ data = JSON.parse(request(:get, "/rest/v1/users/#{username}/concurrency").body)
42
+ data["concurrency"][username]["remaining"]["overall"]
43
+ end
44
+
23
45
  def platforms
24
46
  [].tap do |platforms|
25
47
  config.browsers.each do |name, config|
@@ -63,13 +85,15 @@ module Blade::SauceLabsPlugin::Client
63
85
  browser[:os].flat_map do |browser_os|
64
86
  versions.map do |version|
65
87
  os = platforms.keys.detect { |os| os =~ Regexp.new(browser_os, Regexp::IGNORECASE) }
66
- platforms[os][:api][version].first
88
+ platform = platforms[os][:api][version].first
89
+ { platform: platform[0], browserName: platform[1], version: platform[2] }
67
90
  end
68
91
  end
69
92
  else
70
93
  versions.map do |version|
71
94
  os = platforms.detect { |os, details| details[:api][version].any? }.first
72
- platforms[os][:api][version].first
95
+ platform = platforms[os][:api][version].first
96
+ { platform: platform[0], browserName: platform[1], version: platform[2] }
73
97
  end
74
98
  end
75
99
  end
@@ -0,0 +1,100 @@
1
+ class Blade::SauceLabsPlugin::Job < EventMachine::Completion
2
+ delegate :client, to: Blade::SauceLabsPlugin
3
+
4
+ attr_reader :config, :retries, :web_driver, :session_id
5
+
6
+ def initialize(config, retries = 0)
7
+ super()
8
+ @config = config.symbolize_keys
9
+ @retries = retries
10
+ @web_driver = Blade::SauceLabsPlugin::WebDriver.new(config)
11
+ end
12
+
13
+ def start
14
+ change_state :pending
15
+ web_driver.callback { start_tests }
16
+ web_driver.errback { fail }
17
+ web_driver.start
18
+ end
19
+
20
+ def start_tests
21
+ web_driver.navigate_to(Blade.url) do |success|
22
+ if success
23
+ @id = web_driver.session_id
24
+ @session_id = web_driver.get_cookie(:blade_session)
25
+
26
+ if @id.present? && @session_id.present?
27
+ succeed
28
+ else
29
+ fail
30
+ end
31
+ else
32
+ fail
33
+ end
34
+ end
35
+ end
36
+
37
+ def stop
38
+ return unless completed?
39
+
40
+ web_driver.stop do |success|
41
+ if success
42
+ change_state :stopped
43
+ else
44
+ if client.stop_job(id).success?
45
+ change_state :stopped
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def update(params)
52
+ client.update_job(id, params)
53
+ end
54
+
55
+ def stop_and_delete
56
+ job_id = id
57
+ stop
58
+
59
+ tries = 0
60
+ timer = EM.add_periodic_timer(8) do
61
+ if client.delete_job(job_id).success?
62
+ timer.cancel
63
+ yield true
64
+ else
65
+ tries += 1
66
+ if tries == 10
67
+ timer.cancel
68
+ yield false
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def id
75
+ if @id.present?
76
+ @id
77
+ else
78
+ @found_id ||= find_id
79
+ end
80
+ end
81
+
82
+ private
83
+ def find_id
84
+ match = {
85
+ browser_short_version: config[:version],
86
+ browser: config[:browserName],
87
+ os: config[:platform],
88
+ status: "in progress"
89
+ }
90
+
91
+ client.get_jobs(full: true).each do |job|
92
+ job.symbolize_keys!
93
+ if match.all? { |key, value| job[key] == value }
94
+ return job[:id]
95
+ end
96
+ end
97
+
98
+ nil
99
+ end
100
+ end
@@ -0,0 +1,145 @@
1
+ module Blade::SauceLabsPlugin::JobManager
2
+ extend self
3
+
4
+ Job = Blade::SauceLabsPlugin::Job
5
+
6
+ delegate :config, :client, to: Blade::SauceLabsPlugin
7
+
8
+ cattr_accessor(:job_queue) { EM::Queue.new }
9
+ cattr_accessor(:jobs) { [] }
10
+
11
+ def start
12
+ enqueue_jobs
13
+ process_queue
14
+ handle_completed_jobs
15
+ end
16
+
17
+ def stop
18
+ jobs.each(&:stop)
19
+ end
20
+
21
+ private
22
+ def enqueue_jobs
23
+ client.platforms.each do |platform|
24
+ job_queue << Job.new(platform.merge(test_params))
25
+ end
26
+ end
27
+
28
+ def process_queue
29
+ return if job_queue.empty?
30
+ vm_count = client.get_available_vm_count
31
+
32
+ if vm_count.zero?
33
+ wait_for_available_vm
34
+ else
35
+ vm_count.times do
36
+ job_queue.pop do |job|
37
+ job.callback do
38
+ jobs << job
39
+ end
40
+
41
+ job.errback do
42
+ if job.retries == 3
43
+ exit 1
44
+ else
45
+ job.stop_and_delete do
46
+ job_queue << Job.new(job.config, job.retries + 1)
47
+ EM.next_tick { process_queue }
48
+ end
49
+ end
50
+ end
51
+
52
+ job.start
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def wait_for_available_vm
59
+ @vm_timer ||= EM.add_periodic_timer 3 do
60
+ unless client.get_available_vm_count.zero?
61
+ @vm_timer.cancel
62
+ @vm_timer = nil
63
+ process_queue
64
+ end
65
+ end
66
+ end
67
+
68
+ def handle_completed_jobs
69
+ Blade.subscribe("/results") do |details|
70
+ if details["completed"]
71
+ if job = jobs.detect { |job| job.session_id == details["session_id"] }
72
+ job.stop
73
+ job.update(passed: (details["state"] != "failed"))
74
+ EM.add_timer(1) { process_queue }
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def test_params
81
+ camelize_keys(combined_test_config)
82
+ end
83
+
84
+ def combined_test_config
85
+ default_test_config.merge(env_test_config).merge(test_config).select { |k, v| v.present? }
86
+ end
87
+
88
+ def test_config
89
+ config.test_config || {}
90
+ end
91
+
92
+ def default_test_config
93
+ {
94
+ tunnel_identifier: Blade::SauceLabsPlugin::Tunnel.identifier,
95
+ max_duration: 300,
96
+ name: "Blade Runner CI",
97
+ build: default_build
98
+ }
99
+ end
100
+
101
+ def env_test_config
102
+ {}.tap do |config|
103
+ if build = (get_env_value(:build) || get_env_value(:job_number))
104
+ config[:build] = build
105
+ end
106
+
107
+ tags = []
108
+
109
+ [:commit, :repo_slug, :pull_request].each do |key|
110
+ if tag = tag_from_env(key)
111
+ tags << tag
112
+ end
113
+ end
114
+
115
+ config[:tags] = tags if tags.any?
116
+ end
117
+ end
118
+
119
+ def tag_from_env(key)
120
+ if value = get_env_value(key)
121
+ [key, value].join(":")
122
+ end
123
+ end
124
+
125
+ def get_env_value(key)
126
+ key = key.to_s.upcase
127
+ ENV[key] || ENV["TRAVIS_#{key}"]
128
+ end
129
+
130
+ def default_build
131
+ [rev, Time.now.utc.to_i].select(&:present?).join("-")
132
+ end
133
+
134
+ def rev
135
+ @rev ||= `git rev-parse HEAD 2>/dev/null`.chomp
136
+ end
137
+
138
+ def camelize_keys(hash)
139
+ {}.tap do |result|
140
+ hash.each do |key, value|
141
+ result[key.to_s.camelize(:lower)] = value
142
+ end
143
+ end
144
+ end
145
+ end
@@ -1,5 +1,5 @@
1
1
  module Blade
2
2
  module SauceLabsPlugin
3
- VERSION = "0.4.1"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -0,0 +1,86 @@
1
+ require "selenium/webdriver"
2
+
3
+ class Blade::SauceLabsPlugin::WebDriver < EventMachine::Completion
4
+ class << self
5
+ delegate :username, :access_key, to: Blade::SauceLabsPlugin
6
+ end
7
+
8
+ cattr_accessor(:url ) { "http://#{username}:#{access_key}@ondemand.saucelabs.com:80/wd/hub" }
9
+
10
+ attr_reader :capabilities
11
+ attr_reader :driver
12
+
13
+ def initialize(capabilities)
14
+ super()
15
+ @capabilities = capabilities
16
+ end
17
+
18
+ def start
19
+ EM.defer do
20
+ if @driver = get_driver
21
+ succeed
22
+ else
23
+ fail
24
+ end
25
+ end
26
+ end
27
+
28
+ def stop
29
+ EM.defer do
30
+ yield(quit_driver)
31
+ end
32
+ end
33
+
34
+ def active?
35
+ completed?
36
+ end
37
+
38
+ def navigate_to(url)
39
+ EM.defer do
40
+ begin
41
+ driver.navigate.to url
42
+ sleep 0.5
43
+ yield driver.current_url == url
44
+ rescue
45
+ yield false
46
+ end
47
+ end
48
+ end
49
+
50
+ def session_id
51
+ if active?
52
+ driver.session_id
53
+ end
54
+ end
55
+
56
+ def get_cookie(name)
57
+ if active? && cookie = driver.manage.cookie_named(name.to_s)
58
+ cookie[:value]
59
+ end
60
+ end
61
+
62
+ private
63
+ def get_driver
64
+ driver = Selenium::WebDriver.for(:remote, url: url, http_client: http_client, desired_capabilities: capabilities)
65
+ driver.manage.timeouts.implicit_wait = 10
66
+ driver
67
+ rescue
68
+ nil
69
+ end
70
+
71
+ def quit_driver
72
+ return unless active?
73
+ driver.quit
74
+ true
75
+ rescue
76
+ false
77
+ end
78
+
79
+ def http_client
80
+ @http_client ||= begin
81
+ client = Selenium::WebDriver::Remote::Http::Default.new
82
+ client.timeout = 260
83
+ client
84
+ end
85
+ end
86
+ end
@@ -10,25 +10,41 @@ module Blade::SauceLabsPlugin
10
10
 
11
11
  autoload :Client, "blade/sauce_labs_plugin/client"
12
12
  autoload :Tunnel, "blade/sauce_labs_plugin/tunnel"
13
+ autoload :WebDriver, "blade/sauce_labs_plugin/web_driver"
14
+ autoload :Job, "blade/sauce_labs_plugin/job"
15
+ autoload :JobManager, "blade/sauce_labs_plugin/job_manager"
13
16
 
14
17
  def start
15
18
  if Blade.config.interface == :ci
16
- Tunnel.start do
17
- Blade.config.expected_sessions = Client.platforms.size
18
- Client.request(:post, "rest/v1/#{username}/js-tests", test_params)
19
+ tunnel.start do
20
+ Blade.config.expected_sessions = client.platforms.size
21
+ job_manager.start
19
22
  end
20
23
  end
21
24
  end
22
25
 
23
26
  def stop
24
- Tunnel.stop
27
+ tunnel.stop
28
+ job_manager.stop
25
29
  end
26
30
 
27
31
  # Ensure the tunnel is closed
28
- at_exit { stop }
32
+ at_exit { tunnel.stop }
33
+
34
+ def tunnel
35
+ Tunnel
36
+ end
37
+
38
+ def client
39
+ Client
40
+ end
41
+
42
+ def job_manager
43
+ JobManager
44
+ end
29
45
 
30
46
  def config
31
- Blade.plugins.sauce_labs.config
47
+ Blade.config.plugins.sauce_labs
32
48
  end
33
49
 
34
50
  def username
@@ -48,74 +64,4 @@ module Blade::SauceLabsPlugin
48
64
  def debug?
49
65
  config.debug == true
50
66
  end
51
-
52
- private
53
- def test_params
54
- camelize_keys(combined_test_config)
55
- end
56
-
57
- def combined_test_config
58
- default_test_config.merge(env_test_config).merge(test_config)
59
- end
60
-
61
- def test_config
62
- config.test_config || {}
63
- end
64
-
65
- def default_test_config
66
- {
67
- url: Blade.url,
68
- platforms: Client.platforms,
69
- framework: Blade.config.framework,
70
- tunnel_identifier: Tunnel.identifier,
71
- max_duration: 300,
72
- name: "Blade Runner CI",
73
- build: default_build
74
- }
75
- end
76
-
77
- def env_test_config
78
- {}.tap do |config|
79
- if build = get_env_value(:build_number)
80
- config[:build] = build
81
- end
82
-
83
- tags = []
84
-
85
- [:commit, :repo_slug, :pull_request].each do |key|
86
- if tag = tag_from_env(key)
87
- tags << tag
88
- end
89
- end
90
-
91
- config[:tags] = tags if tags.any?
92
- end
93
- end
94
-
95
- def tag_from_env(key)
96
- if value = get_env_value(key)
97
- [key, value].join(":")
98
- end
99
- end
100
-
101
- def get_env_value(key)
102
- key = key.to_s.upcase
103
- ENV[key].presence || ENV["TRAVIS_#{key}"].presence
104
- end
105
-
106
- def default_build
107
- [rev, Time.now.utc.to_i].select(&:present?).join("-")
108
- end
109
-
110
- def rev
111
- @rev ||= `git rev-parse HEAD 2>/dev/null`.chomp
112
- end
113
-
114
- def camelize_keys(hash)
115
- {}.tap do |result|
116
- hash.each do |key, value|
117
- result[key.to_s.camelize(:lower)] = value
118
- end
119
- end
120
- end
121
67
  end
Binary file
@@ -63,6 +63,7 @@ int sc_stop(struct sc_ctx *ctx);
63
63
  */
64
64
  #define SC_STATUS_RUNNING 0x01
65
65
  #define SC_STATUS_EXITING 0x02
66
+ #define SC_STATUS_STOPPED 0x03
66
67
  int sc_status(struct sc_ctx *ctx);
67
68
 
68
69
  /*
Binary file
@@ -17,7 +17,7 @@ old_library='libsauceconnect.a'
17
17
  inherited_linker_flags=' -pthread'
18
18
 
19
19
  # Libraries that this one depends upon.
20
- dependency_libs=' -L/build/build/Linux/openssl/lib -L/build/curl/ares /build/build/sc-build-1757/lib/libevent.la /build/build/sc-build-1757/lib/libevent_openssl.la /build/build/sc-build-1757/lib/libcares.la /build/build/sc-build-1757/lib/libcurl.la /build/build/sc-build-1757/lib/libcares.la -lz -lrt /build/build/sc-build-1757/lib/libunwind.la -lc -lgcc -lssl -lcrypto -ldl'
20
+ dependency_libs=' -L/build/build/Linux/openssl/lib -L/build/curl/ares /build/build/sc-build-1877/lib/libevent.la /build/build/sc-build-1877/lib/libevent_openssl.la /build/build/sc-build-1877/lib/libcares.la /build/build/sc-build-1877/lib/libcurl.la /build/build/sc-build-1877/lib/libcares.la -lz -lrt /build/build/sc-build-1877/lib/libunwind.la -lc -lgcc -lssl -lcrypto -ldl'
21
21
 
22
22
  # Names of additional weak libraries provided by this library
23
23
  weak_library_names=''
@@ -38,4 +38,4 @@ dlopen=''
38
38
  dlpreopen=''
39
39
 
40
40
  # Directory that this library needs to be installed in:
41
- libdir='/build/build/sc-build-1757/lib'
41
+ libdir='/build/build/sc-build-1877/lib'
Binary file
@@ -63,6 +63,7 @@ int sc_stop(struct sc_ctx *ctx);
63
63
  */
64
64
  #define SC_STATUS_RUNNING 0x01
65
65
  #define SC_STATUS_EXITING 0x02
66
+ #define SC_STATUS_STOPPED 0x03
66
67
  int sc_status(struct sc_ctx *ctx);
67
68
 
68
69
  /*
Binary file
@@ -17,7 +17,7 @@ old_library='libsauceconnect.a'
17
17
  inherited_linker_flags=' -framework CoreFoundation -framework Security'
18
18
 
19
19
  # Libraries that this one depends upon.
20
- dependency_libs=' -L/Users/travis/build/saucelabs/libsauceconnect/build/Darwin/openssl/lib -L/Users/travis/build/saucelabs/libsauceconnect/curl/ares /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib/libevent.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib/libevent_openssl.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib/libcares.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib/libcurl.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib/libcares.la -lz -lssl -lcrypto -ldl'
20
+ dependency_libs=' -L/Users/travis/build/saucelabs/libsauceconnect/build/Darwin/openssl/lib -L/Users/travis/build/saucelabs/libsauceconnect/curl/ares /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib/libevent.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib/libevent_openssl.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib/libcares.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib/libcurl.la /Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib/libcares.la -lz -lssl -lcrypto -ldl'
21
21
 
22
22
  # Names of additional weak libraries provided by this library
23
23
  weak_library_names=''
@@ -38,4 +38,4 @@ dlopen=''
38
38
  dlpreopen=''
39
39
 
40
40
  # Directory that this library needs to be installed in:
41
- libdir='/Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1760/lib'
41
+ libdir='/Users/travis/build/saucelabs/libsauceconnect/build/sc-build-1879/lib'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blade-sauce_labs_plugin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javan Makhmali
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-10-28 00:00:00.000000000 Z
11
+ date: 2016-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: blade
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.5.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: 1.21.0
55
69
  - !ruby/object:Gem::Dependency
56
- name: blade
70
+ name: selenium-webdriver
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -113,8 +127,11 @@ files:
113
127
  - lib/blade/sauce_labs_plugin.rb
114
128
  - lib/blade/sauce_labs_plugin/cli.rb
115
129
  - lib/blade/sauce_labs_plugin/client.rb
130
+ - lib/blade/sauce_labs_plugin/job.rb
131
+ - lib/blade/sauce_labs_plugin/job_manager.rb
116
132
  - lib/blade/sauce_labs_plugin/tunnel.rb
117
133
  - lib/blade/sauce_labs_plugin/version.rb
134
+ - lib/blade/sauce_labs_plugin/web_driver.rb
118
135
  - support/sc-linux/bin/sc
119
136
  - support/sc-linux/include/sauceconnect.h
120
137
  - support/sc-linux/lib/libsauceconnect.a