testswarm-client 0.2.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.rdoc +41 -0
- data/lib/testswarm/client.rb +33 -0
- data/lib/testswarm/job.rb +333 -0
- data/lib/testswarm/project.rb +81 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/testswarm/job_spec.rb +48 -0
- data/spec/testswarm/project_spec.rb +65 -0
- metadata +87 -0
data/README.rdoc
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= TestSwarm::Client
|
2
|
+
|
3
|
+
Simple library for interacting with TestSwarm servers. Makes it easy to create
|
4
|
+
jobs, and may in future allow us to query the TestSwarm server.
|
5
|
+
|
6
|
+
|
7
|
+
== Example
|
8
|
+
|
9
|
+
# Create a client to talk to the server, and get a reference to the project
|
10
|
+
# you want to submit a job to.
|
11
|
+
|
12
|
+
client = TestSwarm::Client.new('http://testswarm.songkick.net')
|
13
|
+
project = client.project('my_project', :auth => 'abc123')
|
14
|
+
|
15
|
+
# Create the job. This checks the project out of version control, creates a
|
16
|
+
# snapshot, builds the project and determines its revision ID.
|
17
|
+
|
18
|
+
job = TestSwarm::Job.create(
|
19
|
+
:rcs => {
|
20
|
+
:type => 'git',
|
21
|
+
:url => 'git://github.com/songkick/foo.git'
|
22
|
+
},
|
23
|
+
|
24
|
+
:directory => "/var/www/testswarm/changeset/#{project.name}",
|
25
|
+
:build => 'coffee -c spec/',
|
26
|
+
:inject => 'spec/*.html'
|
27
|
+
)
|
28
|
+
|
29
|
+
# Add test suites to the job. A test suite has a name and a URL. We can use
|
30
|
+
# the revision number to build the path to our test files.
|
31
|
+
|
32
|
+
path = "#{client.url}/changeset/#{project.name}/#{job.revision}"
|
33
|
+
['FooSpec', 'BarSpec'].each do |spec|
|
34
|
+
job.add_suite(spec, "#{path}/spec/browser.html?spec=#{spec}")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Send the job to the server, giving a name, maximum run count, and which
|
38
|
+
# browsers to run the job in. Returns the TestSwarm job ID if the job is new.
|
39
|
+
|
40
|
+
project.submit_job("My Commit #{job.revision}", job, :max => 5, :browsers => 'all')
|
41
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + '/job'
|
8
|
+
require File.dirname(__FILE__) + '/project'
|
9
|
+
|
10
|
+
module TestSwarm
|
11
|
+
|
12
|
+
DEFAULT_BROWSERS = 'all'
|
13
|
+
DEFAULT_MAX = 1
|
14
|
+
INJECT_SCRIPT = '/js/inject.js'
|
15
|
+
|
16
|
+
class Client
|
17
|
+
attr_reader :url
|
18
|
+
|
19
|
+
def initialize(url)
|
20
|
+
@url = url
|
21
|
+
end
|
22
|
+
|
23
|
+
def project(name, options = {})
|
24
|
+
Project.new(self, name, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def uri
|
28
|
+
@uri ||= URI.parse(@url)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,333 @@
|
|
1
|
+
module TestSwarm
|
2
|
+
class Job
|
3
|
+
|
4
|
+
class AlreadyPrepared < StandardError ; end
|
5
|
+
class MissingConfig < StandardError ; end
|
6
|
+
class FailedCheckout < StandardError ; end
|
7
|
+
class UnknownRevision < StandardError ; end
|
8
|
+
class BuildFailed < StandardError ; end
|
9
|
+
|
10
|
+
def self.create(*args)
|
11
|
+
job = new(*args)
|
12
|
+
job.prepare!
|
13
|
+
job
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(settings)
|
17
|
+
@suites = {}
|
18
|
+
@rcs = settings[:rcs]
|
19
|
+
@directory = settings[:directory]
|
20
|
+
@diff = settings[:diff]
|
21
|
+
@build = settings[:build]
|
22
|
+
@inject = settings[:inject]
|
23
|
+
@keep = settings[:keep]
|
24
|
+
@new = true
|
25
|
+
|
26
|
+
raise MissingConfig, "Required setting :rcs is missing" unless @rcs
|
27
|
+
raise MissingConfig, "Required setting :rcs->:type is missing" unless @rcs[:type]
|
28
|
+
raise MissingConfig, "Required setting :rcs->:url is missing" unless @rcs[:url]
|
29
|
+
raise MissingConfig, "Required setting :directory is missing" unless @directory
|
30
|
+
raise MissingConfig, "Required setting :inject is missing" unless @inject
|
31
|
+
|
32
|
+
@directory = File.expand_path(@directory)
|
33
|
+
|
34
|
+
Kernel.at_exit { close_logfile }
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_suite(name, url)
|
38
|
+
@suites[name] = url
|
39
|
+
end
|
40
|
+
|
41
|
+
def each_suite
|
42
|
+
@suites.keys.sort.each do |name|
|
43
|
+
yield(name, @suites[name])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def new?
|
48
|
+
@new
|
49
|
+
end
|
50
|
+
|
51
|
+
def prepare!
|
52
|
+
raise AlreadyPrepared if @prepared
|
53
|
+
@prepared = true
|
54
|
+
|
55
|
+
@pwd = Dir.pwd
|
56
|
+
|
57
|
+
enter_base_directory
|
58
|
+
|
59
|
+
return @new = false unless acquire_lock
|
60
|
+
|
61
|
+
checkout_codebase
|
62
|
+
determine_revision
|
63
|
+
discard_old_releases
|
64
|
+
|
65
|
+
if existing_job? or not javascript_changed?
|
66
|
+
@new = false
|
67
|
+
return reset
|
68
|
+
end
|
69
|
+
|
70
|
+
build_project
|
71
|
+
reset
|
72
|
+
end
|
73
|
+
|
74
|
+
def revision
|
75
|
+
unless @revision
|
76
|
+
raise UnknownRevision, "Job may not have been prepared"
|
77
|
+
end
|
78
|
+
@revision
|
79
|
+
end
|
80
|
+
|
81
|
+
def inject_script(url)
|
82
|
+
return unless @inject
|
83
|
+
|
84
|
+
log "chdir #{File.join @directory, revision}"
|
85
|
+
Dir.chdir(File.join(@directory, revision))
|
86
|
+
|
87
|
+
Dir.glob(@inject).each do |path|
|
88
|
+
log "Injecting #{url} into #{path}"
|
89
|
+
html = File.read(path)
|
90
|
+
html.gsub! /<\/head>/, %Q{<script>document.write('<scr' + 'ipt src="#{url}?' + (new Date).getTime() + '"><\/scr' + 'ipt>');<\/script><\/head>}
|
91
|
+
File.open(path, 'w') { |f| f.write(html) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def log(message)
|
96
|
+
FileUtils.mkdir_p(@directory)
|
97
|
+
@logfile ||= File.open(File.join(@directory, 'testswarm.log'), 'a')
|
98
|
+
@logfile.sync = true
|
99
|
+
@logfile.puts("[#{Time.now.strftime '%Y-%m-%d %H:%M:%S'}] #{message}")
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def tmp_dir
|
105
|
+
@tmp_dir ||= "tmp-#{Time.now.to_i}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def enter_base_directory
|
109
|
+
log "mkdir -p #{@directory}"
|
110
|
+
FileUtils.mkdir_p(@directory)
|
111
|
+
|
112
|
+
log "chdir #{@directory}"
|
113
|
+
Dir.chdir(@directory)
|
114
|
+
end
|
115
|
+
|
116
|
+
def acquire_lock
|
117
|
+
if File.exist?('.lock')
|
118
|
+
log "Locked, marking job as not new"
|
119
|
+
return false
|
120
|
+
end
|
121
|
+
|
122
|
+
log "Writing lock to #{@directory}/.lock"
|
123
|
+
File.open('.lock', 'w') do |f|
|
124
|
+
f.sync = true
|
125
|
+
f.write('')
|
126
|
+
end
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
def checkout_codebase
|
131
|
+
case @rcs[:type]
|
132
|
+
when 'svn' then checkout_svn_codebase
|
133
|
+
when 'git' then checkout_git_codebase
|
134
|
+
end
|
135
|
+
unless File.exists?(tmp_dir)
|
136
|
+
reset
|
137
|
+
raise FailedCheckout, "Failed to check out code from #{@rcs.inspect}"
|
138
|
+
end
|
139
|
+
log "chdir #{tmp_dir}"
|
140
|
+
Dir.chdir(tmp_dir)
|
141
|
+
end
|
142
|
+
|
143
|
+
def checkout_svn_codebase
|
144
|
+
log "svn co #{@rcs[:url]} #{tmp_dir}"
|
145
|
+
`svn co #{@rcs[:url]} #{tmp_dir}`
|
146
|
+
end
|
147
|
+
|
148
|
+
def checkout_git_codebase
|
149
|
+
log "git clone #{@rcs[:url]} #{tmp_dir}"
|
150
|
+
`git clone #{@rcs[:url]} #{tmp_dir}`
|
151
|
+
|
152
|
+
log "chdir #{tmp_dir}"
|
153
|
+
Dir.chdir(tmp_dir)
|
154
|
+
|
155
|
+
if @rcs[:branch]
|
156
|
+
log "git checkout origin/#{@rcs[:branch]}"
|
157
|
+
`git checkout origin/#{@rcs[:branch]}`
|
158
|
+
end
|
159
|
+
log "git submodule update --init --recursive"
|
160
|
+
`git submodule update --init --recursive`
|
161
|
+
|
162
|
+
log "chdir #{@directory}"
|
163
|
+
Dir.chdir(@directory)
|
164
|
+
end
|
165
|
+
|
166
|
+
def determine_revision
|
167
|
+
@revision = case @rcs[:type]
|
168
|
+
when 'svn' then determine_svn_revision
|
169
|
+
when 'git' then determine_git_revision
|
170
|
+
else ''
|
171
|
+
end
|
172
|
+
|
173
|
+
@revision.strip!
|
174
|
+
log "Revision: #{@revision}"
|
175
|
+
log "Previous: #{@previous_revision}" if @previous_revision
|
176
|
+
|
177
|
+
if @revision.empty?
|
178
|
+
reset
|
179
|
+
raise UnknownRevision, "Could not determine revision"
|
180
|
+
end
|
181
|
+
|
182
|
+
log "chdir #{@directory}"
|
183
|
+
Dir.chdir(@directory)
|
184
|
+
end
|
185
|
+
|
186
|
+
def determine_svn_revision
|
187
|
+
@previous_revision = Dir.entries(@directory).
|
188
|
+
grep(/^\d+$/).
|
189
|
+
sort_by { |s| s.to_i }.
|
190
|
+
last
|
191
|
+
|
192
|
+
log "svn info | grep Revision"
|
193
|
+
`svn info | grep Revision`.gsub(/Revision: /, '')
|
194
|
+
end
|
195
|
+
|
196
|
+
def determine_git_revision
|
197
|
+
latest = latest_git_commits(100)
|
198
|
+
@previous_revision = Dir.entries(@directory).
|
199
|
+
grep(/^[0-9a-f]+$/).
|
200
|
+
sort_by { |s| latest.index(s) || 1000 }.
|
201
|
+
first
|
202
|
+
|
203
|
+
log "git rev-parse --short HEAD"
|
204
|
+
`git rev-parse --short HEAD`
|
205
|
+
end
|
206
|
+
|
207
|
+
def discard_old_releases
|
208
|
+
return unless @keep
|
209
|
+
|
210
|
+
log "chdir #{tmp_dir}"
|
211
|
+
Dir.chdir(tmp_dir)
|
212
|
+
|
213
|
+
latest_commits = case @rcs[:type]
|
214
|
+
when 'svn' then latest_svn_commits(@keep)
|
215
|
+
when 'git' then latest_git_commits(@keep)
|
216
|
+
end
|
217
|
+
|
218
|
+
log "Keeping releases #{latest_commits.join ', '}"
|
219
|
+
|
220
|
+
log "chdir #{@directory}"
|
221
|
+
Dir.chdir(@directory)
|
222
|
+
|
223
|
+
Dir.entries(@directory).each do |entry|
|
224
|
+
next unless entry =~ /^[a-z0-9]+$/i
|
225
|
+
next if latest_commits.include?(entry)
|
226
|
+
|
227
|
+
log "rm -rf #{entry}"
|
228
|
+
FileUtils.rm_rf(entry)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def latest_svn_commits(n)
|
233
|
+
`svn log --limit 5`.strip.
|
234
|
+
split("\n").
|
235
|
+
grep(/^r\d+/).
|
236
|
+
map { |line| line.split(/^r|\s*\|\s*/)[1] }
|
237
|
+
end
|
238
|
+
|
239
|
+
def latest_git_commits(n)
|
240
|
+
`git log --oneline | head -#{n} | cut -d ' ' -f 1`.strip.split("\n")
|
241
|
+
end
|
242
|
+
|
243
|
+
def existing_job?
|
244
|
+
File.exists?(File.join(@directory, @revision))
|
245
|
+
end
|
246
|
+
|
247
|
+
def javascript_changed?
|
248
|
+
return true unless @diff and @previous_revision
|
249
|
+
|
250
|
+
log "chdir #{tmp_dir}"
|
251
|
+
Dir.chdir(tmp_dir)
|
252
|
+
|
253
|
+
[@diff].flatten.each do |pattern|
|
254
|
+
return true if javascript_changed_in?(pattern)
|
255
|
+
end
|
256
|
+
false
|
257
|
+
end
|
258
|
+
|
259
|
+
def javascript_changed_in?(pattern)
|
260
|
+
case @rcs[:type]
|
261
|
+
when 'svn' then javascript_changed_in_svn?(pattern)
|
262
|
+
when 'git' then javascript_changed_in_git?(pattern)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def javascript_changed_in_svn?(pattern)
|
267
|
+
counter = "svn diff -r #{@previous_revision}:#{@revision} | grep 'Index:' | grep '#{pattern}' | wc -l"
|
268
|
+
count = `#{counter}`
|
269
|
+
log "#{counter} -> #{count}"
|
270
|
+
count.strip.to_i > 0
|
271
|
+
end
|
272
|
+
|
273
|
+
def javascript_changed_in_git?(pattern)
|
274
|
+
counter = "git diff --stat #{@previous_revision} HEAD | grep '#{pattern}' | wc -l"
|
275
|
+
count = `#{counter}`
|
276
|
+
log "#{counter} -> #{count}"
|
277
|
+
count.strip.to_i > 0
|
278
|
+
end
|
279
|
+
|
280
|
+
def build_project
|
281
|
+
log "chdir #{@directory}"
|
282
|
+
Dir.chdir(@directory)
|
283
|
+
|
284
|
+
log "mv #{tmp_dir} #{@revision}"
|
285
|
+
FileUtils.mv(tmp_dir, @revision)
|
286
|
+
|
287
|
+
log "chdir #{@revision}"
|
288
|
+
Dir.chdir(@revision)
|
289
|
+
|
290
|
+
return unless @build
|
291
|
+
|
292
|
+
[@build].flatten.each do |step|
|
293
|
+
log step
|
294
|
+
`#{step}`
|
295
|
+
unless $?.exitstatus.zero?
|
296
|
+
reset
|
297
|
+
log "Failed while running #{step}"
|
298
|
+
raise BuildFailed, "Failed while running #{step}"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def reset
|
304
|
+
remove_tmp
|
305
|
+
restore_working_directory
|
306
|
+
release_lock
|
307
|
+
end
|
308
|
+
|
309
|
+
def remove_tmp
|
310
|
+
log "chdir #{@directory}"
|
311
|
+
Dir.chdir(@directory)
|
312
|
+
log "rm -rf #{tmp_dir}"
|
313
|
+
FileUtils.rm_rf(tmp_dir)
|
314
|
+
end
|
315
|
+
|
316
|
+
def restore_working_directory
|
317
|
+
log "chdir #{@pwd}"
|
318
|
+
Dir.chdir(@pwd)
|
319
|
+
end
|
320
|
+
|
321
|
+
def release_lock
|
322
|
+
log "rm #{@directory}/.lock"
|
323
|
+
FileUtils.rm("#{@directory}/.lock")
|
324
|
+
end
|
325
|
+
|
326
|
+
def close_logfile
|
327
|
+
@logfile.close if @logfile
|
328
|
+
@logfile = nil
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module TestSwarm
|
2
|
+
class Project
|
3
|
+
|
4
|
+
class SubmissionFailed < StandardError ; end
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(client, name, options = {})
|
9
|
+
@client = client
|
10
|
+
@name = name
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def payload(job, params = {})
|
15
|
+
cgi = {
|
16
|
+
'action' => 'addjob',
|
17
|
+
'authUsername' => @name,
|
18
|
+
'authToken' => @options[:auth],
|
19
|
+
'jobName' => params[:name],
|
20
|
+
'runMax' => params[:max] || DEFAULT_MAX
|
21
|
+
}
|
22
|
+
|
23
|
+
query = ''
|
24
|
+
cgi.keys.sort.each do |key|
|
25
|
+
query += '&' unless query.empty?
|
26
|
+
query += "#{key}=#{escape cgi[key]}"
|
27
|
+
end
|
28
|
+
|
29
|
+
browsers = [params[:browsers] || DEFAULT_BROWSERS].flatten
|
30
|
+
browsers.each do |browser|
|
31
|
+
query += "&browserSets[]=#{escape browser}"
|
32
|
+
end
|
33
|
+
|
34
|
+
job.each_suite do |name, url|
|
35
|
+
query += "&runNames[]=#{escape name}&runUrls[]=#{escape url}"
|
36
|
+
end
|
37
|
+
query
|
38
|
+
end
|
39
|
+
|
40
|
+
def submit_job(name, job, options = {})
|
41
|
+
job.inject_script(@client.url + INJECT_SCRIPT)
|
42
|
+
|
43
|
+
http = Net::HTTP.start(@client.uri.host, @client.uri.port)
|
44
|
+
data = payload(job, job_params(name, options))
|
45
|
+
|
46
|
+
job.log "POST #{@client.url} #{data}"
|
47
|
+
|
48
|
+
response = http.post('/api.php', data)
|
49
|
+
job.log "Response: #{response.body}"
|
50
|
+
job_data = JSON.parse(response.body)['addjob'] rescue nil
|
51
|
+
|
52
|
+
unless job_data
|
53
|
+
job.log 'Job submission failed'
|
54
|
+
job.log response.body
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
|
58
|
+
job.log "Job ID: #{job_data['id']}"
|
59
|
+
job.log "Runs: #{job_data['runTotal']}, user agents: #{job_data['uaTotal']}"
|
60
|
+
|
61
|
+
job_data['id'].to_s
|
62
|
+
|
63
|
+
rescue => e
|
64
|
+
job.log 'Job submission failed'
|
65
|
+
job.log e.message
|
66
|
+
job.log e.backtrace
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def escape(string)
|
72
|
+
CGI.escape(string.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
def job_params(name, options)
|
76
|
+
options.merge(:name => name)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TestSwarm::Job do
|
4
|
+
let(:params) {{
|
5
|
+
:rcs => {
|
6
|
+
:type => 'git',
|
7
|
+
:url => 'git://github.com/songkick/foo.git'
|
8
|
+
},
|
9
|
+
|
10
|
+
:directory => "/var/www/testswarm/changeset/skweb",
|
11
|
+
:build => 'coffee -c spec/',
|
12
|
+
:inject => 'spec/*.html'
|
13
|
+
}}
|
14
|
+
|
15
|
+
describe :new do
|
16
|
+
def raises_error
|
17
|
+
lambda {
|
18
|
+
TestSwarm::Job.new(params)
|
19
|
+
} .should raise_error(TestSwarm::Job::MissingConfig)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises an error if no RCS is given" do
|
23
|
+
params.delete(:rcs)
|
24
|
+
raises_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises an error if no RCS type is given" do
|
28
|
+
params[:rcs].delete(:type)
|
29
|
+
raises_error
|
30
|
+
end
|
31
|
+
|
32
|
+
it "raises an error if no RCS URL is given" do
|
33
|
+
params[:rcs].delete(:url)
|
34
|
+
raises_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it "raises an error if no directory is given" do
|
38
|
+
params.delete(:directory)
|
39
|
+
raises_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it "raises an error if no inject is given" do
|
43
|
+
params.delete(:inject)
|
44
|
+
raises_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe TestSwarm::Project do
|
4
|
+
let(:uri) { URI.new("http://testswarm.songkick.net") }
|
5
|
+
let(:client) { TestSwarm::Client.new("http://testswarm.songkick.net") }
|
6
|
+
let(:project) { client.project("skweb", :auth => "123abc") }
|
7
|
+
let(:job) { TestSwarm::Job.new(:rcs => {:type => '', :url => ''}, :directory => '', :inject => '') }
|
8
|
+
|
9
|
+
describe :paylooad do
|
10
|
+
before do
|
11
|
+
job.add_suite "Foo", "http://testswarm.songkick.net/changeset/skweb/1f7103f/test/browser.html?spec=FooSpec"
|
12
|
+
job.add_suite "Bar", "http://testswarm.songkick.net/changeset/skweb/1f7103f/test/browser.html?spec=BarSpec"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns CGI-encoded data to be submitted to the server" do
|
16
|
+
project.payload(job, :name => "Job Name").should == [
|
17
|
+
"action=addjob",
|
18
|
+
"authToken=123abc",
|
19
|
+
"authUsername=skweb",
|
20
|
+
"jobName=Job+Name",
|
21
|
+
"runMax=1",
|
22
|
+
"browserSets[]=all",
|
23
|
+
"runNames[]=Bar",
|
24
|
+
"runUrls[]=http%3A%2F%2Ftestswarm.songkick.net%2Fchangeset%2Fskweb%2F1f7103f%2Ftest%2Fbrowser.html%3Fspec%3DBarSpec",
|
25
|
+
"runNames[]=Foo",
|
26
|
+
"runUrls[]=http%3A%2F%2Ftestswarm.songkick.net%2Fchangeset%2Fskweb%2F1f7103f%2Ftest%2Fbrowser.html%3Fspec%3DFooSpec"
|
27
|
+
].join("&")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows defaults to be overridden" do
|
31
|
+
project.payload(job, :browsers => "popular").should =~ /browserSets\[\]=popular/
|
32
|
+
project.payload(job, :max => 5).should =~ /runMax=5/
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe :submit_job do
|
37
|
+
let(:http) { mock Net::HTTP }
|
38
|
+
let(:response) { mock Net::HTTPOK }
|
39
|
+
|
40
|
+
before { job.stub(:inject_script) }
|
41
|
+
|
42
|
+
it "posts the job's CGI payload to the server" do
|
43
|
+
Net::HTTP.should_receive(:start).with("testswarm.songkick.net", 80).and_return(http)
|
44
|
+
params = {:name => "Job Name"}
|
45
|
+
project.should_receive(:payload).with(job, params).and_return("cgi-data")
|
46
|
+
http.should_receive(:post).with("/api.php", "cgi-data").and_return(response)
|
47
|
+
response.should_receive(:body).at_least(1).and_return(%{{"addjob":{"id":1}}})
|
48
|
+
project.submit_job("Job Name", job)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "passes options through when constructing the payload" do
|
52
|
+
params = {:name => "Job Name", :browsers => "beta"}
|
53
|
+
project.should_receive(:payload).with(job, params).and_return("cgi-data")
|
54
|
+
FakeWeb.register_uri(:post, "http://testswarm.songkick.net/api.php", :body => %{{"addjob":{"id":75}}})
|
55
|
+
project.submit_job("Job Name", job, :browsers => "beta")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns the ID of the job" do
|
59
|
+
FakeWeb.register_uri(:post, "http://testswarm.songkick.net/api.php", :body => %{{"addjob":{"id":75,"runTotal":2,"uaTotal":8}}})
|
60
|
+
project.submit_job("Job Name", job).should == "75"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: testswarm-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- James Coglan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fakeweb
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description:
|
47
|
+
email: jcoglan@gmail.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files:
|
51
|
+
- README.rdoc
|
52
|
+
files:
|
53
|
+
- README.rdoc
|
54
|
+
- lib/testswarm/project.rb
|
55
|
+
- lib/testswarm/client.rb
|
56
|
+
- lib/testswarm/job.rb
|
57
|
+
- spec/testswarm/project_spec.rb
|
58
|
+
- spec/testswarm/job_spec.rb
|
59
|
+
- spec/spec_helper.rb
|
60
|
+
homepage:
|
61
|
+
licenses: []
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --main
|
65
|
+
- README.rdoc
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 1.8.23
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: Client library for Mozilla TestSwarm
|
86
|
+
test_files: []
|
87
|
+
has_rdoc:
|