saucelabs-adapter 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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