spassky 0.1.36 → 0.1.37

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,8 +1,42 @@
1
1
  Spassky
2
2
  =======
3
- A distributed web testing tool. We use it at the BBC for testing our web apps on a wide range of devices.
3
+ Spassky is a test framework that allows us to automate the process of running JavaScript Unit tests on various browsers and devices.
4
+ It currently supports running tests in QUnit, but in theory, will support any JavaScript framework that the browser supports.
5
+
6
+ Spassky was a protagonist in the greatest Chess match of last century - which being played by an American and a Soviet Russian, was a Cold War metaphor. Spassky eventually resigned and Fischer won the championship.
7
+
8
+ Architecture
9
+ ------------
10
+ ```
11
+ +-----+
12
+ | You |
13
+ +--+--+
14
+ |
15
+ |
16
+ v
17
+ +------------------------------------------+
18
+ | Spassky Central Server |
19
+ +------------------------------------------+
20
+ | | |
21
+ | | |
22
+ v v v
23
+ +-------+ +-------+ +-----+
24
+ |iPhone3| |iPhone4| |Nokia|
25
+ |-------| |-------| |-----|
26
+ | | | | | +++ |
27
+ | | | | | +++ |
28
+ | + | | + | | +++ |
29
+ +-------+ +-------+ +-----+
30
+ ```
31
+
32
+
33
+
34
+ Developers push JS unit tests to the Spassky server through a command line interface.
35
+ Multiple devices connected to the central Spassky server will poll the server for a suite of tests.
36
+ The browser will be redirected to the test page, run the tests, and then are redirected to the idle loop.
37
+
38
+
4
39
 
5
- ![Spassky](https://github.com/BBC/spassky/raw/master/spassky.jpg)
6
40
 
7
41
  Installation
8
42
  ------------
@@ -11,6 +45,11 @@ Installation
11
45
  gem install spassky
12
46
  ```
13
47
 
48
+
49
+
50
+ ![Spassky](https://github.com/BBC/spassky/raw/master/spassky.jpg)
51
+
52
+
14
53
  Usage
15
54
  -----
16
55
 
@@ -20,7 +59,7 @@ Start the server:
20
59
  spassky server 9191
21
60
  ```
22
61
 
23
- Connect some devices by browsing to http://localhost:9191/device/connect on the device. The device will stay in an idle meta refresh loop until it receives a test to run.
62
+ Connect test devices by browsing to http://localhost:9191/device/connect on the device. The device will stay in an idle meta refresh loop until it receives a test to run.
24
63
 
25
64
  Check what devices are connected to the server:
26
65
 
@@ -52,7 +91,7 @@ We need to run automated tests on a wide range of web-enabled devices with very
52
91
 
53
92
  How it works
54
93
  ------------
55
- Physical devices act as test agents, connected permanently to a central server using meta refresh tags. Using a command-line utility, developers execute tests against those browsers by posting to the central server. The tests themselves are plain HTML pages, that are expected to call an assert URL (e.g. by embedding an image) within a time frame. That means even devices without any JavaScript can run automated tests of some kind.
94
+ Physical devices act as test agents, connected permanently to a central server using meta refresh tags. Using a command-line utility, developers execute tests against those browsers by posting to the central server. The tests themselves are plain HTML pages, that are expected to call an assert URL (e.g. by embedding an image) within a time frame.
56
95
 
57
96
  Test structure
58
97
  --------------
@@ -2,7 +2,7 @@ require 'spassky'
2
2
  require 'spassky/version'
3
3
  require 'spassky/server/app'
4
4
  require 'spassky/client/device_list_retriever'
5
- require 'spassky/client/test_runner'
5
+ require 'spassky/client/test_suite_runner'
6
6
  require 'spassky/client/pusher'
7
7
  require 'spassky/client/directory_reader'
8
8
  require 'commandable'
@@ -18,8 +18,8 @@ module Spassky::Client
18
18
  def run(pattern, test, server = DEFAULT_SERVER, colour = false)
19
19
  writer = colour ? ColouredWriter : DefaultWriter
20
20
  pusher = Pusher.new(server)
21
- test_runner = TestRunner.new(pusher, writer.new(STDOUT), DirectoryReader.new(pattern))
22
- test_runner.run_tests(pattern, test)
21
+ test_suite_runner = TestSuiteRunner.new(pusher, writer.new(STDOUT), DirectoryReader.new(pattern))
22
+ test_suite_runner.run_test_suite(pattern, test)
23
23
  end
24
24
 
25
25
  command "list devices"
@@ -1,31 +1,35 @@
1
1
  module Spassky::Client
2
2
  class DirectoryReader
3
-
4
3
  def initialize(pattern)
5
4
  @pattern = pattern
6
5
  end
7
6
 
7
+ def read_files
8
+ if File.file? @pattern
9
+ read_file
10
+ elsif File.directory? @pattern
11
+ read_directory
12
+ end
13
+ end
14
+
15
+ private
16
+
8
17
  def read_directory
9
- Dir.glob(@pattern + "/**/*").inject({}) do |hash, path|
18
+ files = {}
19
+ Dir.glob(@pattern + "/**/*").each do |path|
10
20
  if File.file? path
11
- key = path.gsub(/^#{@pattern}\//, "")
12
- hash[key] = File.read(path)
21
+ files[remove_pattern_from_file(path)] = File.read(path)
13
22
  end
14
- hash
15
23
  end
24
+ files
25
+ end
16
26
 
27
+ def remove_pattern_from_file path
28
+ path.gsub(/^#{@pattern}\//, "")
17
29
  end
18
30
 
19
31
  def read_file
20
32
  { @pattern => File.read(@pattern) }
21
33
  end
22
-
23
- def read_files
24
- if File.file? @pattern
25
- read_file
26
- elsif File.directory? @pattern
27
- read_directory
28
- end
29
- end
30
34
  end
31
35
  end
@@ -1,5 +1,5 @@
1
1
  require 'rest-client'
2
- require 'spassky/test_result'
2
+ require 'spassky/test_suite_result'
3
3
 
4
4
  module Spassky::Client
5
5
  class Pusher
@@ -9,31 +9,43 @@ module Spassky::Client
9
9
  end
10
10
 
11
11
  def push(options)
12
- location = post_test(options)
13
- result = nil
14
- begin
15
- result = Spassky::TestResult.from_json(RestClient.get(location))
12
+ each_test_suite_result(post_test(options)) do |result|
16
13
  yield result
17
- @sleeper.sleep 0.4 if result.status == 'in progress'
18
- end while result.status == 'in progress'
14
+ @sleeper.sleep 0.4
15
+ end
19
16
  end
20
17
 
21
18
  private
22
19
 
20
+ def each_test_suite_result location
21
+ while (result = get_test_suite_result(location)).status == 'in progress'
22
+ yield result
23
+ end
24
+ yield get_test_suite_result(location)
25
+ end
26
+
27
+ def get_test_suite_result location
28
+ Spassky::TestSuiteResult.from_json(RestClient.get(location))
29
+ end
30
+
23
31
  def test_runs_url
24
32
  test_runs_url = @server_url.gsub(/\/$/, "") + "/test_runs"
25
33
  end
26
34
 
27
- def post_test(options)
35
+ def post_test options
28
36
  RestClient.post(test_runs_url, options) do |response, request, result|
37
+ process_test_post_response response
29
38
  get_redirect_location response
30
39
  end
31
40
  end
32
41
 
33
- def get_redirect_location response
42
+ def process_test_post_response response
34
43
  if response.code == 500
35
44
  raise response.to_str
36
45
  end
46
+ end
47
+
48
+ def get_redirect_location response
37
49
  location = response.headers[:location]
38
50
  raise "Expected #{test_runs_url} to respond with 302" unless location
39
51
  location
@@ -0,0 +1,62 @@
1
+ require 'spassky/client/writer'
2
+
3
+ module Spassky::Client
4
+ class TestSuiteRunner
5
+ def initialize(pusher, writer, directory_reader)
6
+ @pusher = pusher
7
+ @writer = writer
8
+ @directory_reader = directory_reader
9
+ end
10
+
11
+ def run_test_suite(pattern, test_name)
12
+ begin
13
+ @pusher.push(:name => test_name, :contents => @directory_reader.read_files.to_json) do |result|
14
+ handle_test_suite_result(result)
15
+ end
16
+ rescue => error
17
+ fail_with_error error
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def fail_with_error error
24
+ @writer.write_failing(error.message)
25
+ Kernel.exit(1)
26
+ end
27
+
28
+ def handle_test_suite_result(test_suite_result)
29
+ write_in_progress_status test_suite_result
30
+ unless test_suite_result.status == "in progress"
31
+ write(test_suite_result.status, test_suite_result.summary)
32
+ end
33
+ @previous_test_suite_result = test_suite_result
34
+ write_exit_code(test_suite_result)
35
+ end
36
+
37
+ def write_in_progress_status test_suite_result
38
+ test_suite_result.completed_since(@previous_test_suite_result).each do |device_test_status|
39
+ write_completed_test_status device_test_status
40
+ end
41
+ end
42
+
43
+ def write_completed_test_status device_test_status
44
+ write(device_test_status.status, completion_status(device_test_status))
45
+ write(device_test_status.status, device_test_status.message)
46
+ end
47
+
48
+ def completion_status device_test_status
49
+ "#{device_test_status.status.upcase} #{device_test_status.test_name} on #{device_test_status.device_id}"
50
+ end
51
+
52
+ def write status, message
53
+ method = status == 'pass' ? :write_passing : :write_failing
54
+ @writer.send(method, message)
55
+ end
56
+
57
+ def write_exit_code(result)
58
+ Kernel.exit(1) if result.status == 'fail'
59
+ Kernel.exit(2) if result.status == 'timed out'
60
+ end
61
+ end
62
+ end
@@ -16,5 +16,14 @@ module Spassky
16
16
  def completed?
17
17
  @status != "in progress"
18
18
  end
19
+
20
+ def self.from_hash hash
21
+ DeviceTestStatus.new({
22
+ :device_id => hash["device_id"],
23
+ :test_name => hash["test_name"],
24
+ :status => hash["status"],
25
+ :message => hash["message"]}
26
+ )
27
+ end
19
28
  end
20
29
  end
@@ -38,35 +38,22 @@ module Spassky::Server
38
38
  end
39
39
  end
40
40
 
41
- def create_test_run
42
- TestRun.create({
43
- :name => params[:name],
44
- :contents => JSON.parse(params[:contents]),
45
- :devices => @device_list.recently_connected_devices
46
- })
47
- end
48
-
49
41
  post '/test_runs' do
50
42
  recently_connected_devices = @device_list.recently_connected_devices
51
- if recently_connected_devices.size > 0
52
- redirect "/test_runs/#{create_test_run.id}"
53
- else
43
+ if recently_connected_devices.empty?
54
44
  halt 500, "There are no connected devices"
55
45
  end
46
+ redirect "/test_runs/#{create_test_run.id}"
56
47
  end
57
48
 
58
49
  get '/test_runs/:id' do
59
- run = TestRun.find(params[:id])
50
+ run = test_run
60
51
  run.update_connected_devices(@device_list.recently_connected_devices)
61
52
  run.result.to_json
62
53
  end
63
54
 
64
55
  get '/test_runs/:id/run/:random/assert' do
65
- TestRun.find(params[:id]).save_result_for_device(
66
- :device_identifier => get_device_identifier,
67
- :status => params[:status],
68
- :message => params[:message]
69
- )
56
+ save_test_result
70
57
  end
71
58
 
72
59
  get "/test_runs/:id/run/:random/*" do
@@ -76,11 +63,34 @@ module Spassky::Server
76
63
 
77
64
  private
78
65
 
66
+ def test_run
67
+ TestRun.find(params[:id])
68
+ end
69
+
70
+ def create_test_run
71
+ TestRun.create({
72
+ :name => params[:name],
73
+ :contents => test_contents,
74
+ :devices => @device_list.recently_connected_devices
75
+ })
76
+ end
77
+
78
+ def test_contents
79
+ JSON.parse(params[:contents])
80
+ end
81
+
79
82
  def get_test_file_contents test_run_id, file_name
80
- test_run = TestRun.find(params[:id])
81
83
  HtmlTest.new(test_run.contents, idle_url, 1).get_file(file_name)
82
84
  end
83
85
 
86
+ def save_test_result
87
+ test_run.save_result_for_device(
88
+ :device_identifier => get_device_identifier,
89
+ :status => params[:status],
90
+ :message => params[:message]
91
+ )
92
+ end
93
+
84
94
  def redirect_to_run_tests(test_run)
85
95
  redirect "/test_runs/#{test_run.id}/run/#{RandomStringGenerator.random_string}/#{test_run.name}"
86
96
  end
@@ -15,6 +15,7 @@ module Spassky::Server
15
15
  def initialize
16
16
  download_wurfl_file unless File.exist?(WURFL_FILE)
17
17
  @wurfl = WURFL.new(WURFL_FILE)
18
+ @stored_device_identifiers = {}
18
19
  end
19
20
 
20
21
  def download_wurfl_file
@@ -22,27 +23,33 @@ module Spassky::Server
22
23
  Kernel.puts("Downloading WURFL database")
23
24
  RestClient.proxy = ENV["http_proxy"] if ENV["http_proxy"]
24
25
  content = RestClient.get(LATEST)
25
- File.open(WURFL_FILE, "w") do |file|
26
- file.write(content)
27
- end
26
+ save_wurfl_file content
28
27
  end
29
28
 
30
29
  def device_identifier user_agent
31
- @stored_device_identifiers ||= {}
30
+ cached_device_identifier(user_agent) or uncached_device_identifier(user_agent)
31
+ end
32
+
33
+ private
32
34
 
33
- unless @stored_device_identifiers[user_agent]
34
- device = @wurfl[user_agent]
35
- if device.nil?
36
- @stored_device_identifiers[user_agent] = user_agent
37
- else
38
- @stored_device_identifiers[user_agent] = "#{device.model_name} (id = #{device.id}, mobile_browser = #{device.mobile_browser}, device_os_version = #{device.device_os_version})"
39
- end
35
+ def save_wurfl_file content
36
+ File.open(WURFL_FILE, "w") do |file|
37
+ file.write(content)
40
38
  end
39
+ end
40
+
41
+ def cached_device_identifier user_agent
41
42
  @stored_device_identifiers[user_agent]
42
43
  end
43
44
 
44
- def device user_agent
45
- @wurfl[user_agent]
45
+ def uncached_device_identifier user_agent
46
+ @stored_device_identifiers ||= {}
47
+ if device = @wurfl[user_agent]
48
+ @stored_device_identifiers[user_agent] = "#{device.model_name} (id = #{device.id}, mobile_browser = #{device.mobile_browser}, device_os_version = #{device.device_os_version})"
49
+ else
50
+ @stored_device_identifiers[user_agent] = user_agent
51
+ end
52
+ cached_device_identifier user_agent
46
53
  end
47
54
  end
48
55
 
@@ -10,12 +10,18 @@ module Spassky::Server
10
10
 
11
11
  def recently_connected_devices
12
12
  @devices_and_time_last_connected.keys.select do |device_id|
13
- Time.now.to_f - @devices_and_time_last_connected[device_id].to_f < 3
13
+ recent? @devices_and_time_last_connected[device_id]
14
14
  end
15
15
  end
16
16
 
17
17
  def clear
18
18
  @devices_and_time_last_connected = {}
19
19
  end
20
+
21
+ private
22
+
23
+ def recent? time
24
+ Time.now.to_f - time.to_f < 3
25
+ end
20
26
  end
21
27
  end
@@ -1,4 +1,4 @@
1
- require 'spassky/test_result'
1
+ require 'spassky/test_suite_result'
2
2
 
3
3
  module Spassky::Server
4
4
  class TestRun
@@ -19,9 +19,7 @@ module Spassky::Server
19
19
  end
20
20
 
21
21
  def save_result_for_device(options)
22
- unless ['pass', 'fail'].include? options[:status]
23
- raise "#{options[:status]} is not a valid status"
24
- end
22
+ validate_status options[:status]
25
23
  @status_by_device_id[options[:device_identifier]] = options[:status]
26
24
  @message_by_device_id[options[:device_identifier]] = options[:message]
27
25
  end
@@ -35,7 +33,7 @@ module Spassky::Server
35
33
  end
36
34
 
37
35
  def result
38
- Spassky::TestResult.new(@status_by_device_id.map { |device_id, status|
36
+ Spassky::TestSuiteResult.new(@status_by_device_id.map { |device_id, status|
39
37
  Spassky::DeviceTestStatus.new(:device_id => device_id, :status => status, :message => @message_by_device_id[device_id], :test_name => @name)
40
38
  })
41
39
  end
@@ -55,12 +53,20 @@ module Spassky::Server
55
53
  test_runs.find { |test_run| test_run.run_by_device_id?(device_id) == false }
56
54
  end
57
55
 
56
+ private
57
+
58
+ def self.test_runs
59
+ @test_runs ||= []
60
+ end
61
+
58
62
  def self.delete_all
59
63
  @test_runs = []
60
64
  end
61
65
 
62
- def self.test_runs
63
- @test_runs ||= []
66
+ def validate_status status
67
+ unless ['pass', 'fail'].include?(status)
68
+ raise "#{status} is not a valid status"
69
+ end
64
70
  end
65
71
  end
66
72
  end
@@ -0,0 +1,70 @@
1
+ require 'spassky/test_suite_result_summariser'
2
+ require 'spassky/device_test_status'
3
+ require 'json'
4
+
5
+ module Spassky
6
+ class TestSuiteResult
7
+ attr_reader :device_statuses
8
+
9
+ def initialize device_statuses
10
+ @device_statuses = device_statuses
11
+ end
12
+
13
+ def status
14
+ statuses = @device_statuses.map { |s| s.status }
15
+ return "in progress" if statuses.empty?
16
+ ["in progress", "fail", "timed out", "pass"].find {|s| statuses.include? s}
17
+ end
18
+
19
+ def completed_since(older_test_suite_result)
20
+ if older_test_suite_result.nil?
21
+ device_statuses.select { |s| s.completed? }
22
+ else
23
+ find_newly_completed_device_results(older_test_suite_result)
24
+ end
25
+ end
26
+
27
+ def summary
28
+ TestSuiteResultSummariser.new(@device_statuses).summary
29
+ end
30
+
31
+ def to_json
32
+ {
33
+ :status => "pass",
34
+ :device_statuses => @device_statuses.map do |status|
35
+ {
36
+ :device_id => status.device_id,
37
+ :test_name => status.test_name,
38
+ :status => status.status,
39
+ :message => status.message
40
+ }
41
+ end
42
+ }.to_json
43
+ end
44
+
45
+ def self.from_json json
46
+ device_test_statuses = JSON.parse(json)['device_statuses'].map do |device_test_status|
47
+ DeviceTestStatus.from_hash(device_test_status)
48
+ end
49
+ test_suite_result = TestSuiteResult.new(device_test_statuses)
50
+ end
51
+
52
+ private
53
+
54
+ def find_newly_completed_device_results(older_test_suite_result)
55
+ completed = []
56
+ before_and_after(older_test_suite_result) do |before, after|
57
+ if before.in_progress? && after.completed?
58
+ completed << after
59
+ end
60
+ end
61
+ completed
62
+ end
63
+
64
+ def before_and_after(older_test_suite_result)
65
+ device_statuses.each_with_index do |s, i|
66
+ yield older_test_suite_result.device_statuses[i], s
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,17 +1,17 @@
1
1
  module Spassky
2
- class TestResultSummariser
2
+ class TestSuiteResultSummariser
3
3
  def initialize device_statuses
4
4
  @device_statuses = device_statuses
5
5
  end
6
6
 
7
7
  def summary
8
8
  statuses = []
9
- passes = status_count "pass"
10
- fails = status_count "fail"
11
- timeouts = status_count "timed out"
12
- statuses << "#{passes} passed" if passes > 0
13
- statuses << "#{fails} failed" if fails > 0
14
- statuses << "#{timeouts} timed out" if timeouts > 0
9
+ {"pass" => "passed", "fail" => "failed", "timed out" => "timed out"}.each do |status, description|
10
+ status_count = status_count status
11
+ if status_count > 0
12
+ statuses << "#{status_count} #{description}"
13
+ end
14
+ end
15
15
  statuses.join ", "
16
16
  end
17
17
 
@@ -1,3 +1,3 @@
1
1
  module Spassky
2
- VERSION = '0.1.36'
2
+ VERSION = '0.1.37'
3
3
  end
data/spassky.gemspec CHANGED
@@ -31,6 +31,5 @@ Gem::Specification.new do |s|
31
31
  s.add_development_dependency 'capybara'
32
32
  s.add_development_dependency 'aruba'
33
33
  s.add_development_dependency 'fakefs'
34
- s.add_development_dependency 'ruby-debug19'
35
34
  s.add_development_dependency 'factory_girl'
36
35
  end
@@ -8,25 +8,25 @@ module Spassky::Client
8
8
  end
9
9
 
10
10
  let :runner do
11
- mock(:runner, :run_tests => true)
11
+ mock(:runner, :run_test_suite => true)
12
12
  end
13
13
 
14
14
  before do
15
15
  Pusher.stub!(:new).and_return(pusher)
16
- TestRunner.stub!(:new).and_return(runner)
16
+ TestSuiteRunner.stub!(:new).and_return(runner)
17
17
  end
18
18
 
19
19
  describe "spassky run" do
20
20
  context "with a server url" do
21
21
  it "creates a pusher" do
22
22
  Pusher.should_receive(:new).with("server_name").and_return(pusher)
23
- TestRunner.should_receive(:new).with(pusher, anything(), anything()).and_return(runner)
23
+ TestSuiteRunner.should_receive(:new).with(pusher, anything(), anything()).and_return(runner)
24
24
  Cli.new.run "test_pattern", "test_name", "server_name"
25
25
  end
26
26
  end
27
27
 
28
28
  it "runs a test" do
29
- runner.should_receive(:run_tests).with("test_pattern", "test_name")
29
+ runner.should_receive(:run_test_suite).with("test_pattern", "test_name")
30
30
  Cli.new.run "test_pattern", "test_name", "server_name"
31
31
  end
32
32
 
@@ -34,7 +34,7 @@ module Spassky::Client
34
34
  it "creates a test runner with a default writer" do
35
35
  default_writer = mock :default_writer
36
36
  DefaultWriter.should_receive(:new).with(STDOUT).and_return(default_writer)
37
- TestRunner.should_receive(:new).with(anything(), default_writer, anything())
37
+ TestSuiteRunner.should_receive(:new).with(anything(), default_writer, anything())
38
38
  Cli.new.run "test_pattern", "test_name", "server_name"
39
39
  end
40
40
  end
@@ -43,8 +43,8 @@ module Spassky::Client
43
43
  it "creates a test runner with a colour writer" do
44
44
  coloured_writer = mock :coloured_writer
45
45
  ColouredWriter.should_receive(:new).with(STDOUT).and_return(coloured_writer)
46
- TestRunner.should_receive(:new).with(anything(), coloured_writer, anything())
47
- Cli.new.run "test_pattern", "test_name", "server_name", "--colour"
46
+ TestSuiteRunner.should_receive(:new).with(anything(), coloured_writer, anything())
47
+ Cli.new.run "test_pattern", "test_name", "server_name", true
48
48
  end
49
49
  end
50
50
  end
@@ -15,15 +15,15 @@ module Spassky::Client
15
15
  end
16
16
 
17
17
  def in_progress_status
18
- Spassky::TestResult.new([FactoryGirl.build(:device_test_status, :status => 'in progress')]).to_json
18
+ Spassky::TestSuiteResult.new([FactoryGirl.build(:device_test_status, :status => 'in progress')]).to_json
19
19
  end
20
20
 
21
21
  def passed_status
22
- Spassky::TestResult.new([FactoryGirl.build(:device_test_status)]).to_json
22
+ Spassky::TestSuiteResult.new([FactoryGirl.build(:device_test_status)]).to_json
23
23
  end
24
24
 
25
25
  def failed_status
26
- Spassky::TestResult.new([FactoryGirl.build(:device_test_status, :status => 'fail')]).to_json
26
+ Spassky::TestSuiteResult.new([FactoryGirl.build(:device_test_status, :status => 'fail')]).to_json
27
27
  end
28
28
 
29
29
  it "pushes a test to the server" do
@@ -78,7 +78,7 @@ module Spassky::Client
78
78
 
79
79
  it "sleeps while looping during get requests" do
80
80
  RestClient.stub!(:get).and_return(in_progress_status, in_progress_status, in_progress_status, passed_status)
81
- @sleeper.should_receive(:sleep).with(0.4).exactly(3).times
81
+ @sleeper.should_receive(:sleep).with(0.4).exactly(4).times
82
82
  @pusher.push("test contents") { |result| }
83
83
  end
84
84
  end