saucelabs-adapter 0.7.7 → 0.8.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.
data/README.html ADDED
@@ -0,0 +1,180 @@
1
+
2
+ <html>
3
+ <head><title>README.markdown</title></head>
4
+ <body>
5
+ <h1>Saucelabs-Adapter</h1>
6
+
7
+ <p>Saucelabs-adapter provides the glue to connect Rails Selenium tests to saucelabs.com.</p>
8
+
9
+ <p>Currently it supports tests written using Webrat, Polonium and JSUnit.</p>
10
+
11
+ <h2>Getting Started - Webrat or Polonium test suites</h2>
12
+
13
+ <ol>
14
+ <li><p>Prerequisites:</p>
15
+
16
+ <p>You must be able to run selenium tests locally using test/selenium/selenium_suite.rb</p></li>
17
+ <li><p>Install the gem:</p>
18
+
19
+ <pre><code>gem install saucelabs-adapter
20
+ </code></pre></li>
21
+ <li><p>Run the saucelabs_adapter generator in your project:</p>
22
+
23
+ <pre><code>cd your_project
24
+
25
+ script/generate saucelabs_adapter
26
+ </code></pre></li>
27
+ <li><p>Configure it. In config/selenium.yml, replace YOUR-SAUCELABS-USERNAME and
28
+ YOUR-SAUCELABS-ACCESS-KEY with your saucelabs.com account information.</p></li>
29
+ <li><p>Run Tests</p>
30
+
31
+ <p>To run Selenium Test::Unit tests locally:</p>
32
+
33
+ <pre><code>rake selenium:local
34
+ </code></pre>
35
+
36
+ <p>To run Selenium Test::Unit tests using saucelabs.com:</p>
37
+
38
+ <pre><code>rake selenium:sauce
39
+ </code></pre></li>
40
+ </ol>
41
+
42
+
43
+ <h2>Getting Started - JsUnit test suite</h2>
44
+
45
+ <ol>
46
+ <li><p>Prerequisites:</p>
47
+
48
+ <p>Install the latest JsUnit from http://github.com/pivotal/jsunit</p>
49
+
50
+ <p>JsUnit must be installed in RAILS_ROOT/public/jsunit as follows:</p>
51
+
52
+ <pre><code>public/jsunit/jsunit_jar/jsunit.jar -- the compiled jar
53
+ public/jsunit/jsunit/build.xml etc... -- jsunit sources
54
+ </code></pre></li>
55
+ <li><p>Install the saucelabs-adapter gem:</p>
56
+
57
+ <pre><code>gem install saucelabs-adapter
58
+ </code></pre></li>
59
+ <li><p>Run the saucelabs_adapter generator in your project:</p>
60
+
61
+ <pre><code>cd your_project
62
+
63
+ script/generate saucelabs_adapter --jsunit
64
+ </code></pre></li>
65
+ <li><p>Configure it.</p>
66
+
67
+ <p>In config/selenium.yml, replace YOUR-SAUCELABS-USERNAME and
68
+ YOUR-SAUCELABS-ACCESS-KEY with your saucelabs.com account information.</p>
69
+
70
+ <p>Rename RAILS_ROOT/test/jsunit/jsunit_suite_example.rb to RAILS_ROOT/test/jsunit/jsunit_suite.rb
71
+ and modify it if necessary:
72
+ test_page needs to be set to the path under /public where your JsUnit test page (suite.html or similar) lives,
73
+ with '/jsunit' prepended. e.g. if your JsUnit suite runs from RAILS_ROOT/public/javascripts/test-pages/suite.html
74
+ then test_page needs to be set to '/jsunit/javascripts/test-pages/suite.html'.</p></li>
75
+ <li><p>Run Tests</p>
76
+
77
+ <p>To run JsUnit tests locally:</p>
78
+
79
+ <pre><code>rake jsunit:selenium_rc:local
80
+ </code></pre>
81
+
82
+ <p>To run JsUnit tests using saucelabs.com:</p>
83
+
84
+ <pre><code>rake jsunit:selenium_rc:sauce
85
+ </code></pre></li>
86
+ </ol>
87
+
88
+
89
+ <h2>What You Should See</h2>
90
+
91
+ <p>When running tests, intermixed with your test output you should see the following lines:</p>
92
+
93
+ <pre><code> [saucelabs-adapter] Setting up tunnel from Saucelabs (yourhostname-12345.com:80) to localhost:4000
94
+ [saucelabs-adapter] Tunnel ID 717909c571b8319dc5ae708b689fd7f5 for yourhostname-12345.com is up.
95
+ Started
96
+ ....................
97
+ [saucelabs-adapter] Shutting down tunnel to Saucelabs...
98
+ [saucelabs-adapter] done.
99
+ </code></pre>
100
+
101
+ <h2>In Case of Problems</h2>
102
+
103
+ <p>Try setting environment variable SAUCELABS_ADAPTER_DEBUG to "true". This enables more verbose output.</p>
104
+
105
+ <h2>Continuous Integration</h2>
106
+
107
+ <p>Sauce Labs now lets you set the name of a test job.
108
+ By default the SaucelabsAdapter will set this to the name of the machine it is currently running on,
109
+ however you may override this by setting the environment variable SAUCELABS_JOB_NAME.</p>
110
+
111
+ <p>This can be useful if you run many tests from the same CI machine and would like to differentiate between
112
+ them without actually viewing the video.</p>
113
+
114
+ <h2>What it Does</h2>
115
+
116
+ <p>The saucelabs-adapter performs two functions when it detects you are running a test that will use saucelabs.com:</p>
117
+
118
+ <ol>
119
+ <li><p>It sets up a SauceTunnel before the test run starts and tears it down after the test ends. This happens once for the entire test run.</p></li>
120
+ <li><p>It configures the selenium client to connect to the correct address at saucelabs.com. This happens at the start of each test.</p></li>
121
+ </ol>
122
+
123
+ <h1>Resources</h1>
124
+
125
+ <ul>
126
+ <li><a href="http://gemcutter.org/gems/saucelabs-adapter">The gem</a></li>
127
+ <li><a href="http://github.com/pivotal/saucelabs-adapter">Source code</a></li>
128
+ <li><a href="http://www.pivotaltracker.com/projects/59050">Tracker project</a></li>
129
+ <li><a href="http://ci.pivotallabs.com:3333/builds/SaucelabsCanary">Canary CI build</a></li>
130
+ <li><a href="http://github.com/pivotal/saucelabs-canary">Canary project source code</a></li>
131
+ </ul>
132
+
133
+
134
+ <h1>CHANGES</h1>
135
+
136
+ <h2>0.7.6</h2>
137
+
138
+ <ul>
139
+ <li>Added saucelabs_max_duration configuration option.</li>
140
+ </ul>
141
+
142
+
143
+ <h2>0.7.0</h2>
144
+
145
+ <ul>
146
+ <li><p>The gem has been reorganized to better conform with Gem best-practices.</p></li>
147
+ <li><p>The rakefile generator has changed. If you are upgrading, you will need to rerun the generator and overwrite lib/tasks/saucelabs_adapter.rake,
148
+ or just change line 1 of that file to read:</p>
149
+
150
+ <pre><code>require 'saucelabs_adapter/run_utils'
151
+ </code></pre></li>
152
+ <li><p>The selenium.yml syntax has changed to break out all the saucelabs info into separate lines, and the tunnel method is now explicitly stated:</p>
153
+
154
+ <ul>
155
+ <li><p>Old:</p>
156
+
157
+ <pre><code> selenium_browser_key: '{"username": "YOUR-SAUCELABS-USERNAME", "access-key": "YOUR-SAUCELABS-ACCESS-KEY", "os": "Linux", "browser": "firefox", "browser-version": "3."}'
158
+ #
159
+ localhost_app_server_port: "4000"
160
+ tunnel_startup_timeout: 240
161
+ </code></pre></li>
162
+ <li><p>New:</p>
163
+
164
+ <pre><code> saucelabs_username: "YOUR-SAUCELABS-USERNAME"
165
+ saucelabs_access_key: "YOUR-SAUCELABS-ACCESS-KEY"
166
+ saucelabs_browser_os: "Linux"
167
+ saucelabs_browser: "firefox"
168
+ saucelabs_browser_version: "3."
169
+ #
170
+ tunnel_method: :saucetunnel
171
+ tunnel_to_localhost_port: 4000
172
+ tunnel_startup_timeout: 240
173
+ </code></pre></li>
174
+ </ul>
175
+ </li>
176
+ <li><p>The dependency on Python has been removed.</p></li>
177
+ </ul>
178
+
179
+ </body>
180
+ </html>
data/README.markdown CHANGED
@@ -14,7 +14,7 @@ Getting Started - Webrat or Polonium test suites
14
14
 
15
15
  2. Install the gem:
16
16
 
17
- gem install saucelabs-adapter --source gems.pivotallabs.com
17
+ gem install saucelabs-adapter
18
18
 
19
19
  3. Run the saucelabs_adapter generator in your project:
20
20
 
@@ -49,7 +49,7 @@ Getting Started - JsUnit test suite
49
49
 
50
50
  2. Install the saucelabs-adapter gem:
51
51
 
52
- gem install saucelabs-adapter --source gems.pivotallabs.com
52
+ gem install saucelabs-adapter
53
53
 
54
54
  3. Run the saucelabs_adapter generator in your project:
55
55
 
@@ -113,8 +113,21 @@ The saucelabs-adapter performs two functions when it detects you are running a t
113
113
 
114
114
  2. It configures the selenium client to connect to the correct address at saucelabs.com. This happens at the start of each test.
115
115
 
116
- CHANGES
117
- =======
116
+ Resources
117
+ =========
118
+ * [The gem](http://gemcutter.org/gems/saucelabs-adapter)
119
+ * [Source code](http://github.com/pivotal/saucelabs-adapter)
120
+ * [Tracker project](http://www.pivotaltracker.com/projects/59050)
121
+ * [Canary CI build](http://ci.pivotallabs.com:3333/builds/SaucelabsCanary)
122
+ * [Canary project source code](http://github.com/pivotal/saucelabs-canary)
123
+
124
+ NOTABLE CHANGES
125
+ ===============
126
+ 0.8
127
+ ---
128
+ - Added new tunnel type SshTunnel (a generic reverse SSH tunnel), see selenium.yml for now to configure.
129
+ - Added jsunit_polling_interval_seconds configuratin option.
130
+
118
131
  0.7.6
119
132
  -----
120
133
  - Added saucelabs_max_duration configuration option.
data/Rakefile CHANGED
@@ -30,8 +30,12 @@ begin
30
30
  "VERSION",
31
31
  "lib/saucelabs_adapter.rb",
32
32
  "lib/saucelabs-adapter.rb",
33
+ "lib/saucelabs_adapter/utilities.rb",
33
34
  "lib/saucelabs_adapter/run_utils.rb",
34
- "lib/saucelabs_adapter/sauce_tunnel.rb",
35
+ "lib/saucelabs_adapter/tunnel.rb",
36
+ "lib/saucelabs_adapter/tunnels/sauce_tunnel.rb",
37
+ "lib/saucelabs_adapter/tunnels/ssh_tunnel.rb",
38
+ "lib/saucelabs_adapter/tunnels/other_tunnel.rb",
35
39
  "lib/saucelabs_adapter/selenium_config.rb",
36
40
  "lib/saucelabs_adapter/test_unit_adapter.rb",
37
41
  "lib/saucelabs_adapter/jsunit_selenium_support.rb",
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.7
1
+ 0.8.0
@@ -32,12 +32,23 @@ saucelabs: &saucelabs
32
32
  saucelabs_browser_version: "3."
33
33
  saucelabs_max_duration_seconds: 1800
34
34
  # Selenium RC browser connects to and tests the app at this URL:
35
- application_address: "this will be ovewritten if tunnel_method == :saucetunnel"
35
+ application_address: "testhost.com" # this will be ovewritten if tunnel_method == :saucetunnel
36
36
  application_port: 80
37
- # App host is actually a tunnel that tunnels from <application_address>:<application_port> to localhost:<tunnel_to_localhost_port>
38
- tunnel_method: :saucetunnel
39
- tunnel_to_localhost_port: 4000 # Warning: application_port and tunnel_to_localhost_port must be identical if you are using Webrat
40
- tunnel_startup_timeout: 240
37
+ # App host can actually be a tunnel that tunnels from <application_address>:<application_port> to localhost:<tunnel_to_localhost_port>
38
+ # There are 3 kinds of tunnels:
39
+ #
40
+ # tunnel_method: :saucetunnel
41
+ # tunnel_to_localhost_port: 4000 # Warning: application_port and tunnel_to_localhost_port must be identical if you are using Webrat
42
+ # tunnel_startup_timeout: 240
43
+ #
44
+ # tunnel_method: :sshtunnel
45
+ # application_address: proxy.mycompany.com
46
+ # application_port: 12345
47
+ # tunnel_to_localhost_port: 4000 # Warning: application_port and tunnel_to_localhost_port must be identical if you are using Webrat
48
+ # tunnel_username: fred
49
+ # tunnel_keyfile: "/path/to/keyfile" # or tunnel_password: "password"
50
+ #
51
+ # tunnel_method: :othertunnel You're managing your tunnel independently
41
52
 
42
53
  saucelabs_jsunit: &saucelabs_jsunit
43
54
  <<: *saucelabs
@@ -52,6 +63,7 @@ saucelabs_jsunit_ie:
52
63
  saucelabs_browser_os: "Windows 2003"
53
64
  saucelabs_browser: "iexplore"
54
65
  saucelabs_browser_version: "7."
66
+ jsunit_polling_interval_seconds: 300
55
67
 
56
68
  saucelabs_jsunit_safari:
57
69
  <<: *saucelabs_jsunit
@@ -1,5 +1,9 @@
1
+ require 'saucelabs_adapter/utilities'
2
+ require 'saucelabs_adapter/tunnel'
3
+ require 'saucelabs_adapter/tunnels/sauce_tunnel'
4
+ require 'saucelabs_adapter/tunnels/ssh_tunnel'
5
+ require 'saucelabs_adapter/tunnels/other_tunnel'
1
6
  require 'saucelabs_adapter/selenium_config'
2
- require 'saucelabs_adapter/sauce_tunnel'
3
7
  require 'saucelabs_adapter/test_unit_adapter'
4
8
  require 'saucelabs_adapter/jsunit_selenium_support'
5
9
  module SaucelabsAdapter
@@ -1,5 +1,6 @@
1
1
  module SaucelabsAdapter
2
2
  module JsunitSeleniumSupport
3
+ include Utilities
3
4
 
4
5
  def requires
5
6
  require 'saucelabs_adapter/run_utils'
@@ -8,13 +9,14 @@ module SaucelabsAdapter
8
9
  end
9
10
 
10
11
  def setup_jsunit_selenium(options = {})
12
+ @diagnostics_prefix = '[JsunitSeleniumSupport]'
11
13
  requires
12
14
  @selenium_config = SeleniumConfig.new(ENV['SELENIUM_ENV'])
13
15
  start_app_server(options)
14
16
  @selenium_driver = @selenium_config.create_driver(options)
15
- puts "[JsunitSeleniumSupport] calling @selenium_driver.start" if options[:debug]
17
+ debug "calling @selenium_driver.start"
16
18
  @selenium_driver.start
17
- puts "[JsunitSeleniumSupport] @selenium_driver.start done" if options[:debug]
19
+ debug "@selenium_driver.start done"
18
20
  end
19
21
 
20
22
  def teardown_jsunit_selenium
@@ -24,7 +26,7 @@ module SaucelabsAdapter
24
26
 
25
27
  def run_jsunit_test(jsunit_params, options = {})
26
28
  if $:.detect{ |x| x =~ /Selenium/}
27
- raise 'Selenium gem should not be in path! (deprecated in favor of selenium-client, which we require)'
29
+ raise_with_message 'Selenium gem should not be in path! (deprecated in favor of selenium-client, which we require)'
28
30
  end
29
31
 
30
32
  default_jsunit_params = {
@@ -37,6 +39,7 @@ module SaucelabsAdapter
37
39
  jsunit_params.reverse_merge!(default_jsunit_params)
38
40
 
39
41
  test_url = "/jsunit/javascripts/jsunit/jsunit/testRunner.html?" + jsunit_params.map { |k,v| "#{k}=#{v}" }.join("&")
42
+ options.reverse_merge!(:polling_interval => @selenium_config.jsunit_polling_interval_seconds) if @selenium_config.jsunit_polling_interval_seconds
40
43
  run_suite(@selenium_driver, test_url, options)
41
44
  end
42
45
 
@@ -57,7 +60,7 @@ module SaucelabsAdapter
57
60
 
58
61
  def start_app_server(options = {})
59
62
  stop_app_server
60
- puts "[JsunitSeleniumSupport] starting application server:"
63
+ say "starting application server:"
61
64
  app_server_logfile_path = options[:app_server_logfile_path] || "#{RAILS_ROOT}/log/jsunit_jetty_app_server.log"
62
65
  RunUtils.run "ant -f #{RAILS_ROOT}/public/javascripts/jsunit/jsunit/build.xml start_server " +
63
66
  "-Dport=#{local_app_server_port} " +
@@ -66,9 +69,9 @@ module SaucelabsAdapter
66
69
  end
67
70
 
68
71
  def stop_app_server
69
- raise "oops don't know port app server is running on" unless local_app_server_port
72
+ raise_with_message "oops don't know port app server is running on" unless local_app_server_port
70
73
  while Lsof.running?(local_app_server_port)
71
- puts "Killing app server at #{local_app_server_port}..."
74
+ say "Killing app server at #{local_app_server_port}..."
72
75
  Lsof.kill(local_app_server_port)
73
76
  sleep 1
74
77
  end
@@ -76,7 +79,8 @@ module SaucelabsAdapter
76
79
 
77
80
  def run_suite(selenium_driver, suite_path, options = {})
78
81
  default_options = {
79
- :timeout_in_seconds => 1200
82
+ :timeout_in_seconds => 1200,
83
+ :polling_interval => 5
80
84
  }
81
85
  options.reverse_merge!(default_options)
82
86
 
@@ -89,9 +93,10 @@ module SaucelabsAdapter
89
93
  tests_completed = false
90
94
  begin_time = Time.now
91
95
  status = ""
92
- puts "[JsunitSeleniumSupport] Starting to poll JsUnit..." if options[:verbose]
96
+ say "Starting to poll JsUnit (every #{options[:polling_interval]}s)..." if options[:verbose]
93
97
  while (Time.now - begin_time) < options[:jsunit_suite_timeout_seconds] && !tests_completed
94
- sleep 5
98
+ sleep options[:polling_interval]
99
+ debug "polling now...", 2
95
100
  status = selenium_driver.js_eval("window.mainFrame.mainStatus.document.getElementById('content').innerHTML")
96
101
  status.gsub!(/^<[bB]>Status:<\/[bB]> /, '')
97
102
  # Long form: window.frames['mainFrame'].frames['mainCounts'].frames['mainCountsRuns'].document.getElementById('content').innerHTML
@@ -101,18 +106,18 @@ module SaucelabsAdapter
101
106
  run_count = runs.match(/\d+$/)[0].to_i
102
107
  fail_count = fails.match(/\d+$/)[0].to_i
103
108
  error_count = errors.match(/\d+$/)[0].to_i
104
- puts "[JsunitSeleniumSupport] runs/fails/errors: #{run_count}/#{fail_count}/#{error_count} status: #{status}" if options[:verbose]
109
+ say "runs/fails/errors: #{run_count}/#{fail_count}/#{error_count} status: #{status}" if options[:verbose]
105
110
  if status =~ /^Done /
106
111
  tests_completed = true
107
112
  end
108
113
  end
109
- raise "[JsunitSeleniumSupport] Tests failed to complete after #{options[:jsunit_suite_timeout_seconds]}, status was '#{status}'" unless tests_completed
114
+ raise_with_message "Tests failed to complete after #{options[:jsunit_suite_timeout_seconds]}, status was '#{status}'" unless tests_completed
110
115
 
111
- puts "[JsunitSeleniumSupport] ********** JSUnit tests complete, Runs: #{run_count}, Fails: #{fail_count}, Errors: #{error_count} **********"
116
+ say "********** JSUnit tests complete, Runs: #{run_count}, Fails: #{fail_count}, Errors: #{error_count} **********"
112
117
 
113
118
  if (fail_count + error_count > 0)
114
119
  error_messages = selenium_driver.js_eval("window.mainFrame.mainErrors.document.getElementsByName('problemsList')[0].innerHTML")
115
- puts "[JsunitSeleniumSupport] Error messages: #{error_messages}"
120
+ say "Error messages: #{error_messages}"
116
121
  end
117
122
 
118
123
  (fail_count + error_count) == 0
@@ -1,5 +1,8 @@
1
1
  module SaucelabsAdapter
2
2
  class SeleniumConfig
3
+
4
+ include Utilities
5
+
3
6
  attr_reader :configuration
4
7
 
5
8
  def initialize(configuration_name = nil, selenium_yml_path = nil)
@@ -17,7 +20,9 @@ module SaucelabsAdapter
17
20
  :saucelabs_username, :saucelabs_access_key,
18
21
  :saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
19
22
  :saucelabs_max_duration_seconds,
20
- :tunnel_method, :tunnel_to_localhost_port, :tunnel_startup_timeout ].each do |attr|
23
+ :tunnel_method, :tunnel_to_localhost_port, :tunnel_startup_timeout,
24
+ :tunnel_username, :tunnel_password, :tunnel_keyfile,
25
+ :jsunit_polling_interval_seconds ].each do |attr|
21
26
  define_method(attr) do
22
27
  @configuration[attr.to_s]
23
28
  end
@@ -40,7 +45,7 @@ module SaucelabsAdapter
40
45
  end
41
46
 
42
47
  def application_address
43
- if start_sauce_tunnel?
48
+ if start_tunnel? && @configuration['tunnel_method'].to_sym == :saucetunnel
44
49
  # We are using Sauce Labs and Sauce Tunnel.
45
50
  # We need to use a masquerade hostname on the EC2 end of the tunnel that will be unique within the scope of
46
51
  # this account (e.g. pivotallabs). Therefore we mint a fairly unique hostname here.
@@ -78,17 +83,17 @@ module SaucelabsAdapter
78
83
  end
79
84
  end
80
85
 
81
- def create_driver(selenium_args = {}, options = {})
86
+ def create_driver(selenium_args = {})
82
87
  args = selenium_client_driver_args.merge(selenium_args)
83
- puts "[saucelabs-adapter] Connecting to Selenium RC server at #{args[:host]}:#{args[:port]} (testing app at #{args[:url]})" if options[:debug]
84
- puts "[saucelabs-adapter] args = #{args.inspect}" if options[:debug]
88
+ debug "Connecting to Selenium RC server at #{args[:host]}:#{args[:port]} (testing app at #{args[:url]})"
89
+ debug "args = #{args.inspect}"
85
90
  driver = ::Selenium::Client::Driver.new(args)
86
- puts "[saucelabs-adapter] done" if options[:debug]
91
+ debug "done"
87
92
  driver
88
93
  end
89
94
 
90
- def start_sauce_tunnel?
91
- tunnel_method.to_sym == :saucetunnel
95
+ def start_tunnel?
96
+ !tunnel_method.nil? && tunnel_method.to_sym != :othertunnel
92
97
  end
93
98
 
94
99
  def self.parse_yaml(selenium_yml_path)
@@ -111,18 +116,26 @@ module SaucelabsAdapter
111
116
  errors << require_attributes([ :saucelabs_username, :saucelabs_access_key,
112
117
  :saucelabs_browser_os, :saucelabs_browser, :saucelabs_browser_version,
113
118
  :saucelabs_max_duration_seconds ],
114
- "when selenium_server_address is saucelabs.com")
115
- case tunnel_method.to_sym
116
- when nil, ""
117
- when :saucetunnel, :othertunnel
118
- errors << require_attributes([:tunnel_to_localhost_port ],
119
- "if tunnel_method is set")
120
- else
121
- errors << "Unknown tunnel_method: #{tunnel_method}"
119
+ :when => "when selenium_server_address is saucelabs.com")
120
+ if tunnel_method
121
+ errors << require_attributes([:tunnel_to_localhost_port ], :when => "if tunnel_method is set")
122
+ case tunnel_method.to_sym
123
+ when nil, ""
124
+ when :saucetunnel
125
+ when :othertunnel
126
+ errors << require_attributes([:application_address], :when => "when tunnel_method is :othertunnel")
127
+ when :sshtunnel
128
+ errors << require_attributes([:application_address], :when => "when tunnel_method is :sshtunnel")
129
+ errors << require_attributes([:tunnel_password, :tunnel_keyfile],
130
+ :when => "when tunnel_method is :sshtunnel",
131
+ :any_or_all => :any)
132
+ else
133
+ errors << "Unknown tunnel_method: #{tunnel_method}"
134
+ end
122
135
  end
123
136
  else
124
137
  errors << require_attributes([:selenium_browser_key, :application_address ],
125
- "unless server is saucelab.com")
138
+ :when => "unless server is saucelab.com")
126
139
  end
127
140
 
128
141
  errors.flatten!.compact!
@@ -131,11 +144,17 @@ module SaucelabsAdapter
131
144
  end
132
145
  end
133
146
 
134
- def require_attributes(names, under_what_circumstances = "")
147
+ def require_attributes(names, options = {})
148
+ default_options = {
149
+ :when => "",
150
+ :any_or_all => :all
151
+ }
152
+ options.reverse_merge!(default_options)
135
153
  errors = []
136
154
  names.each do |attribute|
137
- errors << "#{attribute} is required #{under_what_circumstances}" if send(attribute).nil?
155
+ errors << "#{attribute} is required #{options[:when]}" if send(attribute).nil?
138
156
  end
157
+ errors = [] if options[:any_or_all] == :any && errors.size < names.size
139
158
  errors
140
159
  end
141
160
 
@@ -6,7 +6,7 @@ if defined?(ActiveSupport)
6
6
  setup :configure_selenium # 'before_setup' callback from ActiveSupport::TestCase
7
7
 
8
8
  def configure_selenium
9
- puts "[saucelabs-adapter] gem is loading..." if ENV['SAUCELABS_ADAPTER_DEBUG']
9
+ puts "[saucelabs-adapter] configuring selenium..." if ENV['SAUCELABS_ADAPTER_DEBUG'] && ENV['SAUCELABS_ADAPTER_DEBUG'].to_i >= 2
10
10
  selenium_config = SaucelabsAdapter::SeleniumConfig.new(ENV['SELENIUM_ENV'])
11
11
  if defined?(Polonium)
12
12
  polonium_config = Polonium::Configuration.instance
@@ -30,7 +30,7 @@ if defined?(Test)
30
30
  def attach_to_mediator_with_sauce_tunnel
31
31
  attach_to_mediator_without_sauce_tunnel
32
32
  @selenium_config = SaucelabsAdapter::SeleniumConfig.new(ENV['SELENIUM_ENV'])
33
- if @selenium_config.start_sauce_tunnel?
33
+ if @selenium_config.start_tunnel?
34
34
  @mediator.add_listener(Test::Unit::UI::TestRunnerMediator::STARTED, &method(:setup_tunnel))
35
35
  @mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED, &method(:teardown_tunnel))
36
36
  end
@@ -39,7 +39,8 @@ if defined?(Test)
39
39
  alias_method_chain :attach_to_mediator, :sauce_tunnel unless private_method_defined?(:attach_to_mediator_without_sauce_tunnel)
40
40
 
41
41
  def setup_tunnel(suite_name)
42
- @tunnel = SaucelabsAdapter::SauceTunnel.new(@selenium_config)
42
+ @tunnel = SaucelabsAdapter::Tunnel.factory(@selenium_config)
43
+ @tunnel.start_tunnel
43
44
  end
44
45
 
45
46
  def teardown_tunnel(suite_name)
@@ -0,0 +1,29 @@
1
+ module SaucelabsAdapter
2
+
3
+ class Tunnel
4
+
5
+ def self.factory(selenium_config)
6
+ tunnels = {
7
+ :saucetunnel => SauceTunnel,
8
+ :sshtunnel => SshTunnel,
9
+ :othertunnel => OtherTunnel
10
+ }
11
+ raise_with_message "Unknown tunnel type #{selenium_config.tunnel_method}" unless tunnels[selenium_config.tunnel_method.to_sym]
12
+
13
+ return tunnels[selenium_config.tunnel_method].new(selenium_config)
14
+ end
15
+
16
+ def initialize(se_config)
17
+ raise "#{self.class.name}.new requires a SeleniumConfig argument" unless se_config.is_a?(SeleniumConfig)
18
+ @se_config = se_config
19
+ end
20
+
21
+ def start_tunnel
22
+ raise "You need to override this method"
23
+ end
24
+
25
+ def shutdown
26
+ raise "You need to override this method"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module SaucelabsAdapter
2
+ class OtherTunnel < Tunnel
3
+
4
+ def start_tunnel; end
5
+ def shutdown; end
6
+ end
7
+ end
@@ -4,17 +4,14 @@ require 'saucerest-ruby/saucerest'
4
4
  require 'saucerest-ruby/gateway'
5
5
 
6
6
  module SaucelabsAdapter
7
- class SauceTunnel
8
- DEFAULT_TUNNEL_STARTUP_TIMEOUT = 240
7
+ class SauceTunnel < Tunnel
9
8
 
10
- def initialize(se_config)
11
- raise "SauceTunnel.new requires a SeleniumConfig argument" unless se_config.is_a?(SeleniumConfig)
12
- @se_config = se_config
13
- connect_to_rest_api
14
- start_tunnel
15
- end
9
+ include Utilities
10
+
11
+ DEFAULT_TUNNEL_STARTUP_TIMEOUT = 240
16
12
 
17
13
  def start_tunnel
14
+ connect_to_rest_api
18
15
  say "Setting up tunnel from Saucelabs (#{@se_config.application_address}:#{@se_config.application_port}) to localhost:#{@se_config.tunnel_to_localhost_port} (timeout #{tunnel_startup_timeout}s)..."
19
16
  boot_tunnel_machine
20
17
  setup_ssh_reverse_tunnel
@@ -72,9 +69,9 @@ module SaucelabsAdapter
72
69
  end
73
70
  rescue Timeout::Error
74
71
  error_message = "Tunnel did not come up in #{tunnel_startup_timeout} seconds."
75
- STDERR.puts "[saucelabs-adapter] " + error_message
72
+ say error_message
76
73
  shutdown_tunnel_machine
77
- raise error_message
74
+ raise_with_message error_message
78
75
  end
79
76
 
80
77
  def shutdown_tunnel_machine
@@ -91,9 +88,9 @@ module SaucelabsAdapter
91
88
  end
92
89
  rescue Timeout::Error
93
90
  # Do not raise here, or else you give false negatives from test runs
94
- STDERR.puts "*" * 80
95
- STDERR.puts "Sauce Tunnel failed to shut down! Go visit http://saucelabs.com/tunnels and shut down the tunnel for #{@se_config.application_address}"
96
- STDERR.puts "*" * 80
91
+ say "*" * 80
92
+ say "Sauce Tunnel failed to shut down! Go visit http://saucelabs.com/tunnels and shut down the tunnel for #{@se_config.application_address}"
93
+ say "*" * 80
97
94
  end
98
95
 
99
96
  def setup_ssh_reverse_tunnel
@@ -110,13 +107,5 @@ module SaucelabsAdapter
110
107
  debug "done."
111
108
  end
112
109
  end
113
-
114
- def say(what)
115
- STDOUT.puts "[saucelabs-adapter] " + what
116
- end
117
-
118
- def debug(what)
119
- STDOUT.puts "[saucelabs-adapter] " + what if ENV['SAUCELABS_ADAPTER_DEBUG']
120
- end
121
110
  end
122
111
  end
@@ -0,0 +1,25 @@
1
+ require 'net/ssh'
2
+ require 'net/ssh/gateway'
3
+ require 'saucerest-ruby/gateway'
4
+
5
+ module SaucelabsAdapter
6
+ class SshTunnel < Tunnel
7
+ include Utilities
8
+
9
+ def start_tunnel
10
+ say "Setting up SSH reverse tunnel from #{@se_config.application_address}:#{@se_config.application_port} to localhost:#{@se_config.tunnel_to_localhost_port}"
11
+ options = @se_config.tunnel_password ? { :password => @se_config.tunnel_password } : { :keys => @se_config.tunnel_keyfile }
12
+ @gateway = Net::SSH::Gateway.new(@se_config.application_address, @se_config.tunnel_username, options)
13
+ @port = @gateway.open_remote(@se_config.tunnel_to_localhost_port.to_i, "127.0.0.1", @se_config.application_port.to_i, "0.0.0.0")
14
+ end
15
+
16
+ def shutdown
17
+ if @gateway
18
+ say "Shutting down ssh reverse tunnel"
19
+ @gateway.close(@port) if @port
20
+ @gateway.shutdown! if @gateway
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module SaucelabsAdapter
2
+ module Utilities
3
+
4
+ def diagnostics_prefix
5
+ @diagnostics_prefix ||= '[saucelabs-adapter]'
6
+ end
7
+
8
+ def say(what)
9
+ STDOUT.puts "#{diagnostics_prefix} #{what}"
10
+ end
11
+
12
+ def debug(what, print_if_level_ge = 0)
13
+ if ENV['SAUCELABS_ADAPTER_DEBUG']
14
+ actual_level = ENV['SAUCELABS_ADAPTER_DEBUG'].to_i
15
+ STDOUT.puts "#{diagnostics_prefix} #{what}" if print_if_level_ge >= actual_level
16
+ end
17
+ end
18
+
19
+ def raise_with_message(message)
20
+ raise "#{diagnostics_prefix} #{message}"
21
+ end
22
+ end
23
+ end
@@ -1,7 +1,10 @@
1
1
  require 'rubygems'
2
- require 'active_support/core_ext/object' # for .blank?
2
+ require 'active_support'
3
+ # Don't pull in the entire saucelabs-adapter otherwise it will complain about: undefined method `setup' for ActiveSupport::TestCase:Class
4
+ # Apparently this is added from outside
5
+ require 'saucelabs_adapter/utilities'
6
+ require 'saucelabs_adapter/selenium_config'
3
7
 
4
- require File.join(File.dirname(__FILE__), '..', 'lib', 'saucelabs_adapter')
5
8
  SELENIUM_YML_FIXTURE_FILE = File.join(File.dirname(__FILE__), 'fixtures', 'selenium.yml')
6
9
 
7
10
  # Doing this to capture args because I seriously doubt we can mock out .new()
@@ -23,9 +26,9 @@ describe "SeleniumConfig" do
23
26
  @selenium_config = SaucelabsAdapter::SeleniumConfig.new('local', SELENIUM_YML_FIXTURE_FILE)
24
27
  end
25
28
 
26
- describe "#start_sauce_tunnel?" do
29
+ describe "#start_tunnel?" do
27
30
  it "should return false" do
28
- @selenium_config.start_sauce_tunnel?.should be_false
31
+ @selenium_config.start_tunnel?.should be_false
29
32
  end
30
33
  end
31
34
 
@@ -73,9 +76,9 @@ describe "SeleniumConfig" do
73
76
  @selenium_config = SaucelabsAdapter::SeleniumConfig.new('stanza_saucelabs_firefox_linux_saucetunnel', SELENIUM_YML_FIXTURE_FILE)
74
77
  end
75
78
 
76
- describe "#start_sauce_tunnel?" do
79
+ describe "#start_tunnel?" do
77
80
  it "should return true" do
78
- @selenium_config.start_sauce_tunnel?.should be_true
81
+ @selenium_config.start_tunnel?.should be_true
79
82
  end
80
83
  end
81
84
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saucelabs-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 8
8
+ - 0
9
+ version: 0.8.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Kelly Felkins, Chad Woolley & Sam Pierson
@@ -9,59 +14,79 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-02-18 00:00:00 -08:00
17
+ date: 2010-02-24 00:00:00 -08:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: rest-client
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 0
23
31
  version: 1.2.0
24
- version:
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: net-ssh
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
30
38
  requirements:
31
39
  - - ">="
32
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 2
43
+ - 0
44
+ - 12
33
45
  version: 2.0.12
34
- version:
46
+ type: :runtime
47
+ version_requirements: *id002
35
48
  - !ruby/object:Gem::Dependency
36
49
  name: net-ssh-gateway
37
- type: :runtime
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
40
52
  requirements:
41
53
  - - ">="
42
54
  - !ruby/object:Gem::Version
55
+ segments:
56
+ - 1
57
+ - 0
58
+ - 1
43
59
  version: 1.0.1
44
- version:
60
+ type: :runtime
61
+ version_requirements: *id003
45
62
  - !ruby/object:Gem::Dependency
46
63
  name: selenium-client
47
- type: :runtime
48
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
50
66
  requirements:
51
67
  - - ">="
52
68
  - !ruby/object:Gem::Version
69
+ segments:
70
+ - 1
71
+ - 2
72
+ - 17
53
73
  version: 1.2.17
54
- version:
74
+ type: :runtime
75
+ version_requirements: *id004
55
76
  - !ruby/object:Gem::Dependency
56
77
  name: lsof
57
- type: :runtime
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
60
80
  requirements:
61
81
  - - ">="
62
82
  - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ - 3
86
+ - 0
63
87
  version: 0.3.0
64
- version:
88
+ type: :runtime
89
+ version_requirements: *id005
65
90
  description: "This gem augments Test::Unit and Polonium/Webrat to run Selenium tests in the cloud. "
66
91
  email: pair+kelly+sam@pivotallabs.com
67
92
  executables: []
@@ -70,6 +95,7 @@ extensions: []
70
95
 
71
96
  extra_rdoc_files:
72
97
  - LICENSE
98
+ - README.html
73
99
  - README.markdown
74
100
  files:
75
101
  - LICENSE
@@ -84,11 +110,16 @@ files:
84
110
  - lib/saucelabs_adapter.rb
85
111
  - lib/saucelabs_adapter/jsunit_selenium_support.rb
86
112
  - lib/saucelabs_adapter/run_utils.rb
87
- - lib/saucelabs_adapter/sauce_tunnel.rb
88
113
  - lib/saucelabs_adapter/selenium_config.rb
89
114
  - lib/saucelabs_adapter/test_unit_adapter.rb
115
+ - lib/saucelabs_adapter/tunnel.rb
116
+ - lib/saucelabs_adapter/tunnels/other_tunnel.rb
117
+ - lib/saucelabs_adapter/tunnels/sauce_tunnel.rb
118
+ - lib/saucelabs_adapter/tunnels/ssh_tunnel.rb
119
+ - lib/saucelabs_adapter/utilities.rb
90
120
  - lib/saucerest-ruby/gateway.rb
91
121
  - lib/saucerest-ruby/saucerest.rb
122
+ - README.html
92
123
  - README.markdown
93
124
  has_rdoc: true
94
125
  homepage: http://github.com/pivotal/saucelabs-adapter
@@ -103,18 +134,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
134
  requirements:
104
135
  - - ">="
105
136
  - !ruby/object:Gem::Version
137
+ segments:
138
+ - 0
106
139
  version: "0"
107
- version:
108
140
  required_rubygems_version: !ruby/object:Gem::Requirement
109
141
  requirements:
110
142
  - - ">="
111
143
  - !ruby/object:Gem::Version
144
+ segments:
145
+ - 0
112
146
  version: "0"
113
- version:
114
147
  requirements: []
115
148
 
116
149
  rubyforge_project:
117
- rubygems_version: 1.3.5
150
+ rubygems_version: 1.3.6
118
151
  signing_key:
119
152
  specification_version: 3
120
153
  summary: Adapter for running Selenium tests using SauceLabs.com