blade-sauce_labs_plugin 0.4.1 → 0.5.0

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 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