sauce 0.3.0 → 0.4.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/VERSION +1 -1
- data/lib/job.rb +68 -62
- data/lib/rest.rb +9 -6
- data/test/test_jobs.rb +107 -27
- data/test/test_jobs_old.rb +58 -0
- data/test/test_tunnels.rb +2 -2
- metadata +3 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/job.rb
CHANGED
@@ -7,21 +7,19 @@ module Sauce
|
|
7
7
|
# Interact with a Sauce Labs selenium jobs as if it were a ruby object
|
8
8
|
class Job
|
9
9
|
|
10
|
-
|
10
|
+
class CannotDeleteJobError < StandardError; end #:nodoc
|
11
11
|
|
12
|
-
attr_accessor :
|
13
|
-
attr_accessor :
|
14
|
-
attr_accessor :creation_time, :
|
15
|
-
attr_accessor :
|
12
|
+
attr_accessor :id, :owner, :status, :error
|
13
|
+
attr_accessor :name, :browser, :browser_version, :os
|
14
|
+
attr_accessor :creation_time, :start_time, :end_time
|
15
|
+
attr_accessor :public, :video_url, :log_url, :tags
|
16
16
|
|
17
|
-
# TODO: Buckets for logs and videos
|
18
|
-
|
19
17
|
# Get the class @@client.
|
20
18
|
# TODO: Consider metaprogramming this away
|
21
19
|
def self.client
|
22
20
|
@@client
|
23
21
|
end
|
24
|
-
|
22
|
+
|
25
23
|
# Set the class @@client.
|
26
24
|
# TODO: Consider metaprogramming this away
|
27
25
|
def self.client=(client)
|
@@ -48,42 +46,28 @@ module Sauce
|
|
48
46
|
self.all.last
|
49
47
|
end
|
50
48
|
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
# Misnomer: Gets the most recent 100 jobs
|
50
|
+
# TODO: Allow/automate paging
|
51
|
+
def self.all(options={})
|
52
|
+
responses = JSON.parse @@client["jobs/full"].get
|
54
53
|
return responses.collect{|response| Sauce::Job.new(response)}
|
55
|
-
=end
|
56
|
-
return self.complete_jobs + self.in_progress_jobs
|
57
54
|
end
|
58
55
|
|
59
56
|
def self.destroy
|
60
57
|
self.all.each { |tunnel| tunnel.destroy }
|
61
58
|
end
|
62
59
|
|
63
|
-
def self.find
|
60
|
+
def self.find(options={})
|
61
|
+
if options.class == String
|
62
|
+
id = options
|
63
|
+
elsif options.class == Hash
|
64
|
+
id = options[:id]
|
65
|
+
end
|
66
|
+
|
64
67
|
#puts "GET-URL: #{@@client.url}jobs/#{id}"
|
65
68
|
Sauce::Job.new JSON.parse(@@client["jobs/#{id}"].get)
|
66
69
|
end
|
67
70
|
|
68
|
-
def self.complete_jobs
|
69
|
-
responses = JSON.parse @@client["complete-jobs"].get
|
70
|
-
start = Time.now
|
71
|
-
jobs = responses["jobs"].collect{|response| Sauce::Job.find(response["id"])}
|
72
|
-
lapsed = Time.now - start
|
73
|
-
puts "Took #{lapsed} seconds"
|
74
|
-
return jobs
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.in_progress_jobs
|
78
|
-
responses = JSON.parse @@client["in-progress-jobs"].get
|
79
|
-
return [] if responses == []
|
80
|
-
start = Time.now
|
81
|
-
jobs = responses["jobs"].collect{|response| Sauce::Job.find(response["id"])}
|
82
|
-
lapsed = Time.now - start
|
83
|
-
puts "Took #{lapsed} seconds"
|
84
|
-
return jobs
|
85
|
-
end
|
86
|
-
|
87
71
|
# Creates an instance representing a job.
|
88
72
|
def initialize(options)
|
89
73
|
build!(options)
|
@@ -92,42 +76,64 @@ module Sauce
|
|
92
76
|
# Retrieves the latest information on this job from the Sauce Labs' server
|
93
77
|
def refresh!
|
94
78
|
response = JSON.parse @@client["jobs/#{@id}"].get
|
95
|
-
puts "\tjob refresh with: #{response
|
79
|
+
#puts "\tjob refresh with: #{response}"
|
96
80
|
build! response
|
97
81
|
self
|
98
82
|
end
|
99
83
|
|
100
|
-
|
84
|
+
# Save/update the current information for the job
|
85
|
+
def save
|
86
|
+
response = JSON.parse(@@client["jobs/#{@id}"]. self.to_json, :content_type => :json, :accept => :json)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.to_json(options={})
|
90
|
+
json = {
|
91
|
+
:id => @id,
|
92
|
+
:owner => @owner,
|
93
|
+
:status => @status,
|
94
|
+
:error => @error,
|
95
|
+
:name => @name,
|
96
|
+
:browser => @browser,
|
97
|
+
:browser_version => @browser_version,
|
98
|
+
:os => @os,
|
99
|
+
:creation_time => @creation_time,
|
100
|
+
:start_time => @start_time,
|
101
|
+
:end_time => @end_time,
|
102
|
+
:video_url => @video_url,
|
103
|
+
:log_url => @log_url,
|
104
|
+
:public => @public,
|
105
|
+
:tags => @tags
|
106
|
+
}
|
107
|
+
|
108
|
+
options[:except].each { |key| json.delete(key) } if options[:except]
|
109
|
+
json = json.select { |key,value| options[:only].include? key } if options[:only]
|
110
|
+
|
111
|
+
return json
|
112
|
+
end
|
113
|
+
|
114
|
+
def delete
|
115
|
+
raise CannonDeleteJobError("Cannot delete jobs via Sauce Labs' REST API currently")
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
101
119
|
|
102
120
|
# Sets all internal variables from a hash
|
103
121
|
def build!(options)
|
104
|
-
|
105
|
-
|
106
|
-
@
|
107
|
-
@
|
108
|
-
@
|
109
|
-
@
|
110
|
-
@
|
111
|
-
@
|
112
|
-
@
|
113
|
-
|
114
|
-
@
|
115
|
-
@
|
116
|
-
@
|
117
|
-
@
|
118
|
-
@
|
119
|
-
|
120
|
-
@os = options["OS"]
|
121
|
-
@browser = options["Browser"]
|
122
|
-
@browser_version = options["BrowserVersion"]
|
123
|
-
|
124
|
-
# TODO: Should this be created_at and updated_at? Probably.
|
125
|
-
@creation_time = options["CreationTime"]
|
126
|
-
@assignment_time = options["AssignmentTime"]
|
127
|
-
@chef_start_time = options["ChefStartTime"]
|
128
|
-
@end_time = options["EndTime"]
|
129
|
-
@modification_time = options["ModificationTime"]
|
130
|
-
@start_time = options["StartTime"]
|
122
|
+
@id = options["id"]
|
123
|
+
@owner = options["owner"]
|
124
|
+
@status = options["status"]
|
125
|
+
@error = options["error"]
|
126
|
+
@name = options["name"]
|
127
|
+
@browser = options["browser"]
|
128
|
+
@browser_version = options["browser_version"]
|
129
|
+
@os = options["os"]
|
130
|
+
@creation_time = options["creation_time"].to_i
|
131
|
+
@start_time = options["start_time"].to_i
|
132
|
+
@end_time = options["end_time"].to_i
|
133
|
+
@video_url = options["video_url"]
|
134
|
+
@log_url = options["log_url"]
|
135
|
+
@public = options["public"]
|
136
|
+
@tags = options["tags"]
|
131
137
|
|
132
138
|
raise NoIDError if @id.nil? or @id.empty?
|
133
139
|
end
|
data/lib/rest.rb
CHANGED
@@ -4,8 +4,8 @@ require 'json'
|
|
4
4
|
module Sauce
|
5
5
|
# The module that brokers most communication with Sauce Labs' REST API
|
6
6
|
class Client
|
7
|
-
class BadAccessError < StandardError; end
|
8
|
-
class MisconfiguredError < StandardError; end
|
7
|
+
class BadAccessError < StandardError; end #:nodoc
|
8
|
+
class MisconfiguredError < StandardError; end #:nodoc
|
9
9
|
|
10
10
|
attr_accessor :username, :access_key, :client, :ip, :api_url
|
11
11
|
attr_accessor :tunnels, :jobs
|
@@ -16,20 +16,23 @@ module Sauce
|
|
16
16
|
@ip = options[:ip]
|
17
17
|
|
18
18
|
raise MisconfiguredError if @username.nil? or @access_key.nil?
|
19
|
-
@api_url = "https://#{@username}:#{@access_key}@saucelabs.com/
|
19
|
+
@api_url = "https://#{@username}:#{@access_key}@saucelabs.com/api/v1/#{@username}/"
|
20
20
|
@client = RestClient::Resource.new @api_url
|
21
21
|
|
22
22
|
@tunnels = Sauce::Tunnel
|
23
23
|
@tunnels.client = @client
|
24
|
-
@tunnels.account = {
|
24
|
+
@tunnels.account = {
|
25
|
+
:username => @username,
|
25
26
|
:access_key => @access_key,
|
26
27
|
:ip => @ip}
|
27
28
|
|
28
29
|
@jobs = Sauce::Job
|
29
30
|
@jobs.client = @client
|
30
|
-
@jobs.account = {
|
31
|
+
@jobs.account = {
|
32
|
+
:username => @username,
|
31
33
|
:access_key => @access_key,
|
32
|
-
:ip => @ip
|
34
|
+
:ip => @ip
|
35
|
+
}
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
data/test/test_jobs.rb
CHANGED
@@ -2,7 +2,7 @@ require 'helper'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
class TestSauce < Test::Unit::TestCase
|
5
|
-
context "A jobs instance" do
|
5
|
+
context "A V1 jobs instance" do
|
6
6
|
setup do
|
7
7
|
# Create this file and put in your details to run the tests
|
8
8
|
account = YAML.load_file "live_account.yml"
|
@@ -11,44 +11,124 @@ class TestSauce < Test::Unit::TestCase
|
|
11
11
|
@ip = account["ip"]
|
12
12
|
@client = Sauce::Client.new(:username => @username,
|
13
13
|
:access_key => @access_key)
|
14
|
+
|
15
|
+
@example_data = YAML.load_file('example_data.yml')
|
14
16
|
end
|
15
17
|
|
16
18
|
should "initialize with passed variables" do
|
17
|
-
job_json = JSON.parse '{"BrowserVersion": "3.", "Name": "example_job/name.rb", "_rev": "5-228269313", "CreationTime": 1266698090, "AssignmentTime": 1266698097, "Server": "192.168.0.1:4443", "AssignedTo": "f663372ba04444ce8cb3e6f61503f304", "ChefStartTime": 1266698101, "EndTime": 1266698139, "Type": "job", "Interactive": "true", "Status": "complete", "SeleniumServerLogUploadRequest": {"bucket": "sauce-userdata", "key": "sgrove/6337fe576deba0ba278dc1b5dfceac5f/selenium-server.log"}, "Tags": ["tag_1", "tag_2"], "ResultId": "6337fe576deba0ba278dc1b5dfceac5f", "AttachmentRequests": {}, "ModificationTime": 1266698139, "Browser": "firefox", "StartTime": 1266698101, "Owner": "sgrove", "_id": "01fc48caba6d15b46fad79e1b0562bbe", "OS": "Linux", "VideoUploadRequest": {"bucket": "sauce-userdata", "key": "sgrove/6337fe576deba0ba278dc1b5dfceac5f/video.flv"}}'
|
18
|
-
|
19
19
|
client = Sauce::Client.new(:username => "test_user",
|
20
20
|
:access_key => "abc123")
|
21
21
|
|
22
|
-
job = client.jobs.new(
|
22
|
+
job = client.jobs.new(JSON.parse(@example_data["example_job"]))
|
23
|
+
|
24
|
+
assert_equal "501aca56282545a9a21ad2fc592b03fa", job.id
|
25
|
+
assert_equal "joe", job.owner
|
26
|
+
assert_equal "complete", job.status
|
27
|
+
assert_equal "job-name", job.name
|
28
|
+
|
29
|
+
assert_equal "firefox", job.browser
|
30
|
+
assert_equal "3.5.", job.browser_version
|
31
|
+
assert_equal "Windows 2003", job.os
|
32
|
+
|
33
|
+
assert_equal 1253856281, job.creation_time
|
34
|
+
assert_equal 1253856366, job.start_time
|
35
|
+
assert_equal 1253856465, job.end_time
|
36
|
+
|
37
|
+
assert_equal "http://saucelabs.com/video/8b6bf8d360cc338cc7cf7f6e093264d0/video.flv", job.video_url
|
38
|
+
assert_equal "http://saucelabs.com/video/8b6bf8d360cc338cc7cf7f6e093264d0/selenium-server.log", job.log_url
|
39
|
+
|
40
|
+
assert_equal false, job.public
|
41
|
+
assert_equal ["test", "example", "python_is_fun"], job.tags
|
42
|
+
end
|
23
43
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
assert_equal "5-228269313", job._rev
|
28
|
-
assert_equal "192.168.0.1:4443", job.server
|
29
|
-
assert_equal "f663372ba04444ce8cb3e6f61503f304", job.assigned_to
|
44
|
+
# Note: This won't pass until we have a canonical "test job" and its data
|
45
|
+
should "retrieve and parse a job via the api" do
|
46
|
+
job = @client.jobs.find("501aca56282545a9a21ad2fc592b03fa")
|
30
47
|
|
31
|
-
assert_equal "
|
32
|
-
assert_equal "
|
48
|
+
assert_equal "501aca56282545a9a21ad2fc592b03fa", job.id
|
49
|
+
assert_equal "joe", job.owner
|
33
50
|
assert_equal "complete", job.status
|
34
|
-
assert_equal
|
35
|
-
|
51
|
+
assert_equal "job-name", job.name
|
52
|
+
|
53
|
+
assert_equal "firefox", job.browser
|
54
|
+
assert_equal "3.5.", job.browser_version
|
55
|
+
assert_equal "Windows 2003", job.os
|
56
|
+
|
57
|
+
assert_equal 1253856281, job.creation_time
|
58
|
+
assert_equal 1253856366, job.start_time
|
59
|
+
assert_equal 1253856465, job.end_time
|
60
|
+
|
61
|
+
assert_equal "http://saucelabs.com/video/8b6bf8d360cc338cc7cf7f6e093264d0/video.flv", job.video_url
|
62
|
+
assert_equal "http://saucelabs.com/video/8b6bf8d360cc338cc7cf7f6e093264d0/selenium-server.log", job.log_url
|
63
|
+
|
64
|
+
assert_equal false, job.public
|
65
|
+
assert_equal ["test", "example", "python_is_fun"], job.tags
|
66
|
+
end
|
67
|
+
|
68
|
+
should "update writable properties" do
|
69
|
+
job = @client.jobs.find("501aca56282545a9a21ad2fc592b03fa")
|
70
|
+
|
71
|
+
# Make sure it's in the expected condition before changing
|
72
|
+
assert_equal false, job.public
|
73
|
+
assert_equal ["test", "example", "python_is_fun"], job.tags
|
74
|
+
assert_equal "job-name", job.name
|
36
75
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
76
|
+
job.public = true
|
77
|
+
job.tags = ["changed", "updated", "ruby_is_also_fun"]
|
78
|
+
job.name = "changed-job-name", job.name
|
79
|
+
job.save
|
41
80
|
|
81
|
+
# Fresh copy of the same job
|
82
|
+
job2 = @client.jobs.find("501aca56282545a9a21ad2fc592b03fa")
|
83
|
+
|
84
|
+
assert_equal true, job.public
|
85
|
+
assert_equal ["changed", "updated", "ruby_is_also_fun"], job.tags
|
86
|
+
assert_equal "changed-job-name", job.name
|
87
|
+
|
88
|
+
# Return the job to its original state and check it out
|
89
|
+
job.public = false
|
90
|
+
job.tags = ["test", "example", "python_is_fun"]
|
91
|
+
job.name = "job-name", job.name
|
92
|
+
job.save
|
93
|
+
|
94
|
+
# Check to see if the change took
|
95
|
+
job2.refresh!
|
96
|
+
assert_equal job.public, job2.public
|
97
|
+
assert_equal job.tags, job2.tags
|
98
|
+
assert_equal job.name, job2.name
|
99
|
+
end
|
100
|
+
|
101
|
+
should "not update read-only properties" do
|
102
|
+
job = @client.jobs.find("501aca56282545a9a21ad2fc592b03fa")
|
103
|
+
|
104
|
+
# Make sure it's in the expected condition before changing
|
105
|
+
assert_equal "complete", job.status
|
106
|
+
assert_equal "joe", job.owner
|
42
107
|
assert_equal "Linux", job.os
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
assert_equal
|
48
|
-
assert_equal
|
49
|
-
assert_equal
|
50
|
-
|
51
|
-
|
108
|
+
|
109
|
+
job.status = "groggy"
|
110
|
+
job.owner = "sjobs"
|
111
|
+
job.os = "darwin" # In a perfect world...
|
112
|
+
assert_equal "groggy", job.status
|
113
|
+
assert_equal "joe", job.owner
|
114
|
+
assert_equal "darwin", job.os
|
115
|
+
job.save
|
116
|
+
|
117
|
+
# Changes should go away when refreshed
|
118
|
+
job.refresh!
|
119
|
+
assert_equal "complete", job.status
|
120
|
+
assert_equal "joe", job.owner
|
121
|
+
assert_equal "Linux", job.os
|
122
|
+
end
|
123
|
+
|
124
|
+
should "list the 100 most recent jobs" do
|
125
|
+
jobs = @client.jobs.all
|
126
|
+
|
127
|
+
assert_equal 5, jobs.count
|
128
|
+
end
|
129
|
+
|
130
|
+
should "show the full job information on index if requested" do
|
131
|
+
flunk "TODO: implement this"
|
52
132
|
end
|
53
133
|
|
54
134
|
def teardown
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class TestSauce < Test::Unit::TestCase
|
5
|
+
context "A jobs instance" do
|
6
|
+
setup do
|
7
|
+
# Create this file and put in your details to run the tests
|
8
|
+
account = YAML.load_file "live_account.yml"
|
9
|
+
@username = account["username"]
|
10
|
+
@access_key = account["access_key"]
|
11
|
+
@ip = account["ip"]
|
12
|
+
@client = Sauce::Client.new(:username => @username,
|
13
|
+
:access_key => @access_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "initialize with passed variables" do
|
17
|
+
job_json = JSON.parse '{"BrowserVersion": "3.", "Name": "example_job/name.rb", "_rev": "5-228269313", "CreationTime": 1266698090, "AssignmentTime": 1266698097, "Server": "192.168.0.1:4443", "AssignedTo": "f663372ba04444ce8cb3e6f61503f304", "ChefStartTime": 1266698101, "EndTime": 1266698139, "Type": "job", "Interactive": "true", "Status": "complete", "SeleniumServerLogUploadRequest": {"bucket": "sauce-userdata", "key": "sgrove/6337fe576deba0ba278dc1b5dfceac5f/selenium-server.log"}, "Tags": ["tag_1", "tag_2"], "ResultId": "6337fe576deba0ba278dc1b5dfceac5f", "AttachmentRequests": {}, "ModificationTime": 1266698139, "Browser": "firefox", "StartTime": 1266698101, "Owner": "sgrove", "_id": "01fc48caba6d15b46fad79e1b0562bbe", "OS": "Linux", "VideoUploadRequest": {"bucket": "sauce-userdata", "key": "sgrove/6337fe576deba0ba278dc1b5dfceac5f/video.flv"}}'
|
18
|
+
|
19
|
+
client = Sauce::Client.new(:username => "test_user",
|
20
|
+
:access_key => "abc123")
|
21
|
+
|
22
|
+
job = client.jobs.new(job_json)
|
23
|
+
|
24
|
+
assert_equal "sgrove", job.owner
|
25
|
+
assert_equal "01fc48caba6d15b46fad79e1b0562bbe", job.id
|
26
|
+
assert_equal "example_job/name.rb", job.name
|
27
|
+
assert_equal "5-228269313", job._rev
|
28
|
+
assert_equal "192.168.0.1:4443", job.server
|
29
|
+
assert_equal "f663372ba04444ce8cb3e6f61503f304", job.assigned_to
|
30
|
+
|
31
|
+
assert_equal "job", job.sauce_type
|
32
|
+
assert_equal "true", job.interactive
|
33
|
+
assert_equal "complete", job.status
|
34
|
+
assert_equal ["tag_1", "tag_2"], job.tags
|
35
|
+
assert_equal "6337fe576deba0ba278dc1b5dfceac5f", job.result_id
|
36
|
+
|
37
|
+
# TODO: Buckets
|
38
|
+
#assert_equal , job.selenium_server_log_upload_request
|
39
|
+
#assert_equal , job.attachment_requests
|
40
|
+
#assert_equal , job.videoupload_request
|
41
|
+
|
42
|
+
assert_equal "Linux", job.os
|
43
|
+
assert_equal "firefox", job.browser
|
44
|
+
assert_equal "3.", job.browser_version
|
45
|
+
|
46
|
+
assert_equal 1266698090, job.creation_time
|
47
|
+
assert_equal 1266698097, job.assignment_time
|
48
|
+
assert_equal 1266698101, job.chef_start_time
|
49
|
+
assert_equal 1266698139, job.end_time
|
50
|
+
assert_equal 1266698139, job.modification_time
|
51
|
+
assert_equal 1266698101, job.start_time
|
52
|
+
end
|
53
|
+
|
54
|
+
def teardown
|
55
|
+
@client.tunnels.destroy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/test/test_tunnels.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class TestSauce < Test::Unit::TestCase
|
4
|
-
context "A
|
4
|
+
context "A V1 tunnel instance" do
|
5
5
|
setup do
|
6
6
|
# Create this file and put in your details to run the tests
|
7
7
|
account = YAML.load_file "live_account.yml"
|
@@ -16,7 +16,7 @@ class TestSauce < Test::Unit::TestCase
|
|
16
16
|
should "initialize with passed variables" do
|
17
17
|
client = Sauce::Client.new(:username => "test_user",
|
18
18
|
:access_key => "abc123")
|
19
|
-
assert_equal
|
19
|
+
assert_equal "https://test_user:abc123@saucelabs.com/api/v1/test_user/", client.api_url
|
20
20
|
end
|
21
21
|
|
22
22
|
should "create a tunnel with the current user" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sauce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Grove
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02
|
12
|
+
date: 2010-03-02 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -88,5 +88,6 @@ test_files:
|
|
88
88
|
- test/helper.rb
|
89
89
|
- test/irb_boot.rb
|
90
90
|
- test/test_jobs.rb
|
91
|
+
- test/test_jobs_old.rb
|
91
92
|
- test/test_tunnels.rb
|
92
93
|
- test/test_videos.rb
|