dynport_tools 0.2.6 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/VERSION +1 -1
- data/dynport_tools.gemspec +10 -2
- data/lib/dynport_tools.rb +1 -1
- data/lib/dynport_tools/eta.rb +63 -0
- data/lib/dynport_tools/jenkins.rb +65 -0
- data/lib/dynport_tools/redis_q.rb +75 -0
- data/spec/dynport_tools/eta_spec.rb +114 -0
- data/spec/dynport_tools/jenkins_spec.rb +150 -3
- data/spec/dynport_tools/redis_q_spec.rb +177 -0
- data/spec/dynport_tools/xml_file_spec.rb +2 -3
- data/spec/spec_helper.rb +33 -2
- metadata +77 -58
data/.autotest
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -33,6 +33,7 @@ GEM
|
|
33
33
|
ruby-debug-base (~> 0.10.4.0)
|
34
34
|
ruby-debug-base (0.10.4)
|
35
35
|
linecache (>= 0.3)
|
36
|
+
timecop (0.3.5)
|
36
37
|
typhoeus (0.2.4)
|
37
38
|
mime-types
|
38
39
|
mime-types
|
@@ -51,4 +52,5 @@ DEPENDENCIES
|
|
51
52
|
redis
|
52
53
|
rspec (~> 2.3.0)
|
53
54
|
ruby-debug
|
55
|
+
timecop
|
54
56
|
typhoeus
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.8
|
data/dynport_tools.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dynport_tools}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Tobias Schwab"]
|
12
|
-
s.date = %q{2011-08-
|
12
|
+
s.date = %q{2011-08-23}
|
13
13
|
s.description = %q{Collection of various tools}
|
14
14
|
s.email = %q{tobias.schwab@dynport.de}
|
15
15
|
s.executables = ["xmldiff", "redis_dumper"]
|
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
"README.rdoc"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
|
+
".autotest",
|
21
22
|
".document",
|
22
23
|
".rbenv-version",
|
23
24
|
".rspec",
|
@@ -34,15 +35,19 @@ Gem::Specification.new do |s|
|
|
34
35
|
"lib/dynport_tools.rb",
|
35
36
|
"lib/dynport_tools/deep_merger.rb",
|
36
37
|
"lib/dynport_tools/differ.rb",
|
38
|
+
"lib/dynport_tools/eta.rb",
|
37
39
|
"lib/dynport_tools/have_attributes.rb",
|
38
40
|
"lib/dynport_tools/jenkins.rb",
|
39
41
|
"lib/dynport_tools/redis_dumper.rb",
|
42
|
+
"lib/dynport_tools/redis_q.rb",
|
40
43
|
"lib/dynport_tools/xml_file.rb",
|
41
44
|
"spec/dynport_tools/deep_merger_spec.rb",
|
42
45
|
"spec/dynport_tools/differ_spec.rb",
|
46
|
+
"spec/dynport_tools/eta_spec.rb",
|
43
47
|
"spec/dynport_tools/have_attributes_spec.rb",
|
44
48
|
"spec/dynport_tools/jenkins_spec.rb",
|
45
49
|
"spec/dynport_tools/redis_dumper_spec.rb",
|
50
|
+
"spec/dynport_tools/redis_q_spec.rb",
|
46
51
|
"spec/dynport_tools/xml_file_spec.rb",
|
47
52
|
"spec/dynport_tools_spec.rb",
|
48
53
|
"spec/fixtures/file_a.xml",
|
@@ -71,6 +76,7 @@ Gem::Specification.new do |s|
|
|
71
76
|
s.add_development_dependency(%q<autotest>, [">= 0"])
|
72
77
|
s.add_development_dependency(%q<autotest-growl>, [">= 0"])
|
73
78
|
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
79
|
+
s.add_development_dependency(%q<timecop>, [">= 0"])
|
74
80
|
else
|
75
81
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
76
82
|
s.add_dependency(%q<redis>, [">= 0"])
|
@@ -83,6 +89,7 @@ Gem::Specification.new do |s|
|
|
83
89
|
s.add_dependency(%q<autotest>, [">= 0"])
|
84
90
|
s.add_dependency(%q<autotest-growl>, [">= 0"])
|
85
91
|
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
92
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
86
93
|
end
|
87
94
|
else
|
88
95
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
@@ -96,6 +103,7 @@ Gem::Specification.new do |s|
|
|
96
103
|
s.add_dependency(%q<autotest>, [">= 0"])
|
97
104
|
s.add_dependency(%q<autotest-growl>, [">= 0"])
|
98
105
|
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
106
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
99
107
|
end
|
100
108
|
end
|
101
109
|
|
data/lib/dynport_tools.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
class DynportTools::ETA
|
2
|
+
attr_accessor :total, :current, :started
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
options.each do |key, value|
|
6
|
+
self.send(:"#{key}=", value) if self.respond_to?(:"#{key}=")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def percs
|
11
|
+
raise_error_when_current_or_total_not_set
|
12
|
+
current.to_f / total
|
13
|
+
end
|
14
|
+
|
15
|
+
def pending
|
16
|
+
raise_error_when_current_or_total_not_set
|
17
|
+
total - current
|
18
|
+
end
|
19
|
+
|
20
|
+
def running_for
|
21
|
+
Time.now - started
|
22
|
+
end
|
23
|
+
|
24
|
+
def total_time
|
25
|
+
running_for / percs
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_go
|
29
|
+
total_time - running_for
|
30
|
+
end
|
31
|
+
|
32
|
+
def eta
|
33
|
+
Time.now + to_go
|
34
|
+
end
|
35
|
+
|
36
|
+
def per_second
|
37
|
+
current / running_for
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"%.1f%%, %.1f/second, ETA: %s" % [percs * 100, per_second, eta.iso8601]
|
42
|
+
end
|
43
|
+
|
44
|
+
def raise_error_when_current_or_total_not_set
|
45
|
+
raise "current and total must be set" if total.nil? || current.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
FACTORS = [1, 60, 3600]
|
50
|
+
|
51
|
+
def parse_time_string(string)
|
52
|
+
sum = 0.0
|
53
|
+
string.split(":").map { |s| s.to_i }.reverse.each_with_index do |value, i|
|
54
|
+
sum += value * FACTORS[i]
|
55
|
+
end
|
56
|
+
sum
|
57
|
+
end
|
58
|
+
|
59
|
+
def from_time_string(string, options = {})
|
60
|
+
self.new(options.merge(:started => Time.now - parse_time_string(string)))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -34,4 +34,69 @@ class DynportTools::Jenkins
|
|
34
34
|
hydra.run
|
35
35
|
jobs
|
36
36
|
end
|
37
|
+
|
38
|
+
class Project
|
39
|
+
attr_accessor :name, :commands, :crontab_pattern, :days_to_keep, :num_to_keep, :node, :child_projects
|
40
|
+
DEFAUL_SCM = "hudson.scm.NullSCM"
|
41
|
+
|
42
|
+
def initialize(name)
|
43
|
+
self.name = name
|
44
|
+
self.commands = []
|
45
|
+
self.child_projects = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_xml
|
49
|
+
Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
|
50
|
+
xml.project do
|
51
|
+
xml.actions
|
52
|
+
xml.description
|
53
|
+
if days_to_keep || num_to_keep
|
54
|
+
xml.logRotator do
|
55
|
+
xml.daysToKeep days_to_keep || -1
|
56
|
+
xml.numToKeep num_to_keep || -1
|
57
|
+
xml.artifactDaysToKeep -1
|
58
|
+
xml.artifactNumToKeep -1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
xml.keepDependencies "false"
|
62
|
+
xml.properties
|
63
|
+
xml.scm(:class => DEFAUL_SCM)
|
64
|
+
xml.assignedNode node if node
|
65
|
+
xml.canRoam "true"
|
66
|
+
xml.disabled "false"
|
67
|
+
xml.blockBuildWhenDownstreamBuilding "false"
|
68
|
+
xml.blockBuildWhenUpstreamBuilding "false"
|
69
|
+
xml.triggers(:class => "vector") do
|
70
|
+
if crontab_pattern
|
71
|
+
xml.send("hudson.triggers.TimerTrigger") do
|
72
|
+
xml.spec crontab_pattern
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
xml.concurrentBuild "false"
|
77
|
+
xml.builders do
|
78
|
+
commands.each do |command|
|
79
|
+
xml.send("hudson.tasks.Shell") do
|
80
|
+
xml.command ["#!/bin/sh", command].join("\n")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
xml.publishers do
|
85
|
+
if child_projects.any?
|
86
|
+
xml.send("hudson.tasks.BuildTrigger") do
|
87
|
+
xml.childProjects child_projects.map { |c| c.name }.join(",")
|
88
|
+
xml.threshold do
|
89
|
+
xml.name "SUCCESS"
|
90
|
+
xml.ordinal "0"
|
91
|
+
xml.color "BLUE"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
xml.buildWrappers do
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end.to_xml
|
100
|
+
end
|
101
|
+
end
|
37
102
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class DynportTools::RedisQ
|
2
|
+
DEFAULTS = { :retry_count => 3 }
|
3
|
+
attr_accessor :redis_key, :retry_count, :redis
|
4
|
+
|
5
|
+
def initialize(redis_key, options = {})
|
6
|
+
DEFAULTS.merge(options).merge(:redis_key => redis_key).each do |key, value|
|
7
|
+
self.send(:"#{key}=", value) if self.respond_to?(:"#{key}=")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def push(id, priority = nil, options = {})
|
12
|
+
priority ||= Time.now.to_i * -1
|
13
|
+
if nil_or_lower?(priority_of(id), priority)
|
14
|
+
redis.multi if !options[:no_multi]
|
15
|
+
redis.zrem(failed_key, id) if !options[:failed]
|
16
|
+
redis.zadd(redis_key, priority, id)
|
17
|
+
redis.exec if !options[:no_multi]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def push_many(array, options = {})
|
22
|
+
redis.multi do
|
23
|
+
array.each do | (id, popularity) |
|
24
|
+
push(id, popularity, options.merge(:no_multi => true))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def nil_or_lower?(a, b)
|
30
|
+
a.nil? || a.to_i < b
|
31
|
+
end
|
32
|
+
|
33
|
+
def priority_of(id)
|
34
|
+
redis.zscore(redis_key, id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def count
|
38
|
+
redis.zcard(redis_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def pop
|
42
|
+
redis.multi do
|
43
|
+
redis.zrevrange(redis_key, 0, 0, :with_scores => true)
|
44
|
+
redis.zremrangebyrank(redis_key, -1, -1)
|
45
|
+
end.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def failed_tries
|
49
|
+
@failed_tries ||= Hash.new(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
def each
|
53
|
+
entries_with_errors = []
|
54
|
+
stats = { :errors => {}, :ok => [] }
|
55
|
+
while (result = pop).any?
|
56
|
+
begin
|
57
|
+
yield(result.first)
|
58
|
+
stats[:ok] << result.first
|
59
|
+
rescue => err
|
60
|
+
stats[:errors][result.first] = ([err.message] + err.backtrace[0,5]).join("\n")
|
61
|
+
entries_with_errors << result if mark_failed(result.first) < retry_count
|
62
|
+
end
|
63
|
+
end
|
64
|
+
push_many(entries_with_errors, :failed => true) if entries_with_errors.any?
|
65
|
+
stats
|
66
|
+
end
|
67
|
+
|
68
|
+
def mark_failed(id)
|
69
|
+
redis.zincrby(failed_key, 1, id).to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
def failed_key
|
73
|
+
"#{redis_key}/failed_counts"
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
describe "DynportTools::ETA" do
|
5
|
+
let(:eta) { DynportTools::ETA.new(:current => 17, :total => 100) }
|
6
|
+
let(:time) { Time.parse("2011-02-03 04:50") }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Timecop.freeze(time)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can be initialized" do
|
13
|
+
DynportTools::ETA.new
|
14
|
+
end
|
15
|
+
|
16
|
+
it "allows setting of total" do
|
17
|
+
eta.total = 10
|
18
|
+
eta.total.should == 10
|
19
|
+
end
|
20
|
+
|
21
|
+
it "allos_setting of current" do
|
22
|
+
eta.current = 10
|
23
|
+
eta.current.should == 10
|
24
|
+
end
|
25
|
+
|
26
|
+
it "allows setting of started" do
|
27
|
+
eta.started = "some value"
|
28
|
+
eta.started.should == "some value"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "allows setting of all value sthrough initalizer" do
|
32
|
+
eta = DynportTools::ETA.new(:total => 10, :current => 1, :started => "started")
|
33
|
+
eta.should return_values(:total => 10, :current => 1, :started => "started")
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#parse_time_string" do
|
37
|
+
{
|
38
|
+
"00:15:26" => 926,
|
39
|
+
"00:00:26" => 26,
|
40
|
+
"1:00:26" => 3626,
|
41
|
+
}.each do |from, to|
|
42
|
+
it "returns #{to} for #{from}" do
|
43
|
+
DynportTools::ETA.parse_time_string(from).should == to
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#percs" do
|
49
|
+
it "returns the correct value for percs" do
|
50
|
+
eta.total = 100
|
51
|
+
eta.current = 62
|
52
|
+
eta.percs.should == 0.62
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raises an error when total is not set" do
|
56
|
+
eta.total = nil
|
57
|
+
eta.current = 10
|
58
|
+
lambda {
|
59
|
+
eta.percs
|
60
|
+
}.should raise_error("current and total must be set")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "raises an error when current is not set" do
|
64
|
+
eta.current = nil
|
65
|
+
eta.total = 10
|
66
|
+
lambda {
|
67
|
+
eta.percs
|
68
|
+
}.should raise_error("current and total must be set")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#pending" do
|
73
|
+
it "returns teh correct amount for pending" do
|
74
|
+
eta.pending.should == 83
|
75
|
+
end
|
76
|
+
|
77
|
+
it "calls raise_error_when_current_or_total_not_set" do
|
78
|
+
eta.should_receive(:raise_error_when_current_or_total_not_set)
|
79
|
+
eta.pending
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "returns the correct amount for running_for" do
|
84
|
+
DynportTools::ETA.new(:current => 17, :total => 100, :started => time - 99).running_for.should == 99
|
85
|
+
end
|
86
|
+
|
87
|
+
it "returns the correct amount of total time" do
|
88
|
+
DynportTools::ETA.new(:current => 10, :total => 100, :started => time - 10).total_time.should == 100
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns the correct amount of time to go" do
|
92
|
+
DynportTools::ETA.new(:current => 10, :total => 100, :started => time - 10).to_go.should == 90
|
93
|
+
end
|
94
|
+
|
95
|
+
it "returns the correct eta" do
|
96
|
+
DynportTools::ETA.new(:current => 10, :total => 100, :started => time - 10).eta.should == time + 90
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns the correct value for per_second" do
|
100
|
+
DynportTools::ETA.new(:current => 10, :total => 100, :started => time - 1).per_second.should == 10
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns the correct string" do
|
104
|
+
DynportTools::ETA.new(:current => 10, :total => 100, :started => time - 1).to_s.should == "10.0%, 10.0/second, ETA: 2011-02-03T04:50:09+01:00"
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#from_time_string" do
|
108
|
+
it "sets the correct values" do
|
109
|
+
eta = DynportTools::ETA.from_time_string("00:01:10", :total => 100)
|
110
|
+
eta.should be_kind_of(DynportTools::ETA)
|
111
|
+
eta.should return_values(:started => time - 70, :total => 100)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -3,8 +3,155 @@ require 'spec_helper'
|
|
3
3
|
require "dynport_tools/jenkins"
|
4
4
|
|
5
5
|
describe "DynportTools::Jenkins" do
|
6
|
-
let(:
|
7
|
-
let(:jenkins) { DynportTools::Jenkins.new(
|
6
|
+
let(:url) { "http://some.url.com:8098" }
|
7
|
+
let(:jenkins) { DynportTools::Jenkins.new(url) }
|
8
|
+
|
9
|
+
describe "Project" do
|
10
|
+
let(:job) { DynportTools::Jenkins::Project.new("Some Name") }
|
11
|
+
let(:doc) { Nokogiri::XML(job.to_xml) }
|
12
|
+
|
13
|
+
describe "#initialize" do
|
14
|
+
it "sets the commands to an empty array" do
|
15
|
+
job.commands.should == []
|
16
|
+
end
|
17
|
+
|
18
|
+
it "sets the commands to an empty array" do
|
19
|
+
job.child_projects.should == []
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets the name" do
|
23
|
+
job.name.should == "Some Name"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#to_xml" do
|
28
|
+
it "returns a string" do
|
29
|
+
job.to_xml.should be_kind_of(String)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "includes a xml header line" do
|
33
|
+
job.to_xml.should include(%(<?xml version="1.0" encoding="UTF-8"?>))
|
34
|
+
end
|
35
|
+
|
36
|
+
it "includes a project root" do
|
37
|
+
job.to_xml.should include("<project>")
|
38
|
+
job.to_xml.should include("</project>")
|
39
|
+
end
|
40
|
+
|
41
|
+
%w(actions description properties builders publishers buildWrappers).each do |key|
|
42
|
+
it "includes an empty node #{key}" do
|
43
|
+
doc.at("/project/#{key}").children.should be_empty
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
{
|
48
|
+
"keepDependencies" => "false",
|
49
|
+
"canRoam" => "true",
|
50
|
+
"disabled" => "false",
|
51
|
+
"blockBuildWhenDownstreamBuilding" => "false",
|
52
|
+
"blockBuildWhenUpstreamBuilding" => "false",
|
53
|
+
"concurrentBuild" => "false"
|
54
|
+
}.each do |key, value|
|
55
|
+
it "sets #{key} to #{value}" do
|
56
|
+
doc.at("/project/#{key}").inner_text.should == value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
{ "scm" => "hudson.scm.NullSCM", "triggers" => "vector" }.each do |key, clazz|
|
61
|
+
it "sets the class of #{key} to #{clazz}" do
|
62
|
+
doc.at("/project/#{key}")["class"].should == clazz
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "includes all set commands" do
|
67
|
+
job.commands << "hostname"
|
68
|
+
job.commands << "date"
|
69
|
+
shell_tasks = doc.search("project/builders/*")
|
70
|
+
shell_tasks.map(&:name).should == ["hudson.tasks.Shell", "hudson.tasks.Shell"]
|
71
|
+
shell_tasks.map { |node| node.at("command").inner_text }.should == ["#!/bin/sh\nhostname", "#!/bin/sh\ndate"]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "includes crontab like triggers" do
|
75
|
+
pattern = "0 2 * * *"
|
76
|
+
job.crontab_pattern = pattern
|
77
|
+
triggers = doc.search("project/triggers/*")
|
78
|
+
triggers.map(&:name).should == ["hudson.triggers.TimerTrigger"]
|
79
|
+
triggers.first.at("spec").inner_text.should == "0 2 * * *"
|
80
|
+
end
|
81
|
+
|
82
|
+
%w(logRotator assignedNode).each do |key|
|
83
|
+
it "does not include a #{key} node by default" do
|
84
|
+
doc.at("/project/#{key}").should be_nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "sets assignedNode when node is set" do
|
89
|
+
job.node = "processor"
|
90
|
+
doc.at("/project/assignedNode").inner_text.should == "processor"
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "with days_to_keep set" do
|
94
|
+
before(:each) do
|
95
|
+
job.days_to_keep = 7
|
96
|
+
end
|
97
|
+
|
98
|
+
it "sets days_to_keep to 7" do
|
99
|
+
doc.at("/project/logRotator/daysToKeep").inner_text.should == "7"
|
100
|
+
end
|
101
|
+
|
102
|
+
%w(numToKeep artifactDaysToKeep artifactNumToKeep).each do |key|
|
103
|
+
it "sets #{key} to -1" do
|
104
|
+
doc.at("/project/logRotator/#{key}").inner_text.should == "-1"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "with num_to_keep set" do
|
110
|
+
before(:each) do
|
111
|
+
job.num_to_keep = 30
|
112
|
+
end
|
113
|
+
|
114
|
+
it "sets num_to_keep to 30" do
|
115
|
+
doc.at("/project/logRotator/numToKeep").inner_text.should == "30"
|
116
|
+
end
|
117
|
+
|
118
|
+
%w(daysToKeep artifactDaysToKeep artifactNumToKeep).each do |key|
|
119
|
+
it "sets #{key} to -1" do
|
120
|
+
doc.at("/project/logRotator/#{key}").inner_text.should == "-1"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "sets numToKeep and daysToKeep when both set" do
|
126
|
+
job.num_to_keep = 10
|
127
|
+
job.days_to_keep = 2
|
128
|
+
doc.at("/project/logRotator/numToKeep").inner_text.should == "10"
|
129
|
+
doc.at("/project/logRotator/daysToKeep").inner_text.should == "2"
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "with child projects" do
|
133
|
+
let(:child1) { DynportTools::Jenkins::Project.new("child 1") }
|
134
|
+
let(:child2) { DynportTools::Jenkins::Project.new("child 2") }
|
135
|
+
let(:triggers) { doc.xpath("/project/publishers/hudson.tasks.BuildTrigger") }
|
136
|
+
|
137
|
+
before(:each) do
|
138
|
+
job.child_projects << child2
|
139
|
+
job.child_projects << child1
|
140
|
+
end
|
141
|
+
|
142
|
+
it "includes all child projects" do
|
143
|
+
triggers.count.should == 1
|
144
|
+
triggers.first.at("childProjects").inner_text.should == "child 2,child 1"
|
145
|
+
end
|
146
|
+
|
147
|
+
{ "name" => "SUCCESS", "ordinal" => "0", "color" => "BLUE" }.each do |key, value|
|
148
|
+
it "sets #{key} to #{value} in threshold" do
|
149
|
+
triggers.first.at("threshold/#{key}").inner_text.should == value
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
8
155
|
|
9
156
|
describe "#initialize" do
|
10
157
|
it "sets the root url" do
|
@@ -113,7 +260,7 @@ describe "DynportTools::Jenkins" do
|
|
113
260
|
end
|
114
261
|
}
|
115
262
|
end
|
116
|
-
builder.to_xml.should == Nokogiri::XML(File.read(
|
263
|
+
builder.to_xml.should == Nokogiri::XML(File.read(root.join("spec/fixtures/jenkins_job.xml"))).to_s
|
117
264
|
end
|
118
265
|
end
|
119
266
|
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DynportTools::RedisQ do
|
4
|
+
let(:key) { "test/redis_queue" }
|
5
|
+
let(:redis) { Redis.new(:path => root.join("tmp/redis.socket")) }
|
6
|
+
let(:queue) do
|
7
|
+
q = DynportTools::RedisQ.new(key)
|
8
|
+
q.redis = redis
|
9
|
+
q
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
redis.del(key)
|
14
|
+
redis.del("test/redis_queue/failed_counts")
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#initialize" do
|
18
|
+
it "sets the retry_count to the default value when nil" do
|
19
|
+
DynportTools::RedisQ.new("some/queue").retry_count.should == 3
|
20
|
+
end
|
21
|
+
|
22
|
+
it "sets the retry_count to a custom value when given" do
|
23
|
+
DynportTools::RedisQ.new("some/queue", :retry_count => 2).retry_count.should == 2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sets the redis_key" do
|
27
|
+
DynportTools::RedisQ.new("some/queue").redis_key.should == "some/queue"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "sets the redis connection" do
|
31
|
+
DynportTools::RedisQ.new("some/queue", :redis => "redis con").redis.should == "redis con"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "sets the redis key when initializing" do
|
36
|
+
queue = DynportTools::RedisQ.new("some/queue")
|
37
|
+
queue.redis_key.should == "some/queue"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#push" do
|
41
|
+
it "pushes the records with negative timestamps" do
|
42
|
+
Timecop.freeze(Time.at(112233))
|
43
|
+
queue.push(99)
|
44
|
+
Timecop.freeze(Time.at(112235))
|
45
|
+
queue.push(101)
|
46
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["99", "-112233", "101", "-112235"]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "does not push records when already on the queue" do
|
50
|
+
Timecop.freeze(Time.at(112233))
|
51
|
+
queue.push(99)
|
52
|
+
Timecop.freeze(Time.at(112235))
|
53
|
+
queue.push(99)
|
54
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["99", "-112233"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "changes the priority of a member when higher" do
|
58
|
+
queue.push(99, 1)
|
59
|
+
queue.push(99, 2)
|
60
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["99", "2"]
|
61
|
+
end
|
62
|
+
|
63
|
+
it "uses a custom priority when given" do
|
64
|
+
queue.push(99, 1234)
|
65
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["99", "1234"]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "removes the key from the failed zset" do
|
69
|
+
queue.mark_failed("99")
|
70
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == ["99", "1"]
|
71
|
+
queue.push(99, 1234)
|
72
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == []
|
73
|
+
end
|
74
|
+
|
75
|
+
it "does not call redis.multi when no_multi is set to true" do
|
76
|
+
redis.should_not_receive(:multi)
|
77
|
+
queue.push(99, 1234, :no_multi => true)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "does not call redis.zrem when failed is true" do
|
81
|
+
redis.should_not_receive(:zrem)
|
82
|
+
queue.push(99, 1234, :failed => true)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns the correct number of elements" do
|
87
|
+
queue.count.should == 0
|
88
|
+
queue.push(99, 1234)
|
89
|
+
queue.push(101, 1234)
|
90
|
+
queue.count.should == 2
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#pop" do
|
94
|
+
before(:each) do
|
95
|
+
queue.push(98, 10)
|
96
|
+
queue.push(99, 1)
|
97
|
+
queue.push(101, 100)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns the highest member and rank" do
|
101
|
+
queue.pop.should == ["101", "100"]
|
102
|
+
end
|
103
|
+
|
104
|
+
it "removes the member from the set" do
|
105
|
+
queue.pop
|
106
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["98", "10", "99", "1"]
|
107
|
+
end
|
108
|
+
|
109
|
+
it "returns an empty array when nothing in the queue" do
|
110
|
+
queue.pop
|
111
|
+
queue.pop
|
112
|
+
queue.pop
|
113
|
+
queue.pop.should == []
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#each" do
|
118
|
+
it "calls the block with all members in the queue" do
|
119
|
+
queue.push(99, 1)
|
120
|
+
queue.push(100, 9)
|
121
|
+
queue.push(101, 0)
|
122
|
+
ids = []
|
123
|
+
queue.each do |id|
|
124
|
+
ids << id
|
125
|
+
end
|
126
|
+
ids.should == ["100", "99", "101"]
|
127
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == []
|
128
|
+
end
|
129
|
+
|
130
|
+
it "readds all ids to the queue which raised errors" do
|
131
|
+
queue.push(99, 1)
|
132
|
+
queue.push(100, 9)
|
133
|
+
queue.push(101, 0)
|
134
|
+
processor = double("processor")
|
135
|
+
processor.should_receive(:process).with("99").and_return true
|
136
|
+
processor.should_receive(:process).with("101").and_return true
|
137
|
+
processor.should_receive(:process).with("100").and_raise("some error")
|
138
|
+
stats = queue.each do |id|
|
139
|
+
processor.process(id)
|
140
|
+
end
|
141
|
+
stats[:ok].should == ["99", "101"]
|
142
|
+
stats[:errors]["100"].should match(/^some error/)
|
143
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["100", "9"]
|
144
|
+
end
|
145
|
+
|
146
|
+
# change this to n times
|
147
|
+
it "only tries to process one failing id 3 times" do
|
148
|
+
queue.push(100, 9)
|
149
|
+
processor = double("processor")
|
150
|
+
processor.stub(:process).with("100").exactly(3).and_raise("some error")
|
151
|
+
|
152
|
+
stats = queue.each { |id| processor.process(id) }
|
153
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["100", "9"]
|
154
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == ["100", "1"]
|
155
|
+
|
156
|
+
stats = queue.each { |id| processor.process(id) }
|
157
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == ["100", "9"]
|
158
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == ["100", "2"]
|
159
|
+
|
160
|
+
stats = queue.each { |id| processor.process(id) }
|
161
|
+
redis.zrevrange(key, 0, -1, :with_scores => true).should == []
|
162
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == ["100", "3"]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "#mark_failed" do
|
167
|
+
it "increments the counter in the zset" do
|
168
|
+
queue.mark_failed("100")
|
169
|
+
redis.zrevrange("test/redis_queue/failed_counts", 0, -1, :with_scores => true).should == ["100", "1"]
|
170
|
+
end
|
171
|
+
|
172
|
+
it "returns the new failed count" do
|
173
|
+
queue.mark_failed("100").should == 1
|
174
|
+
queue.mark_failed("100").should == 2
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require "dynport_tools/xml_file"
|
3
3
|
|
4
|
-
FILE1 =
|
5
|
-
FILE2 =
|
6
|
-
|
4
|
+
FILE1 = root.join("spec/fixtures/file_a.xml")
|
5
|
+
FILE2 = root.join("spec/fixtures/file_b.xml")
|
7
6
|
|
8
7
|
describe "DynportTools::XmlFile" do
|
9
8
|
let(:file) { DynportTools::XmlFile.new(FILE1) }
|
data/spec/spec_helper.rb
CHANGED
@@ -2,13 +2,44 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
3
|
require 'rspec'
|
4
4
|
require 'dynport_tools'
|
5
|
+
require "timecop"
|
5
6
|
|
6
7
|
# Requires supporting files with custom matchers and macros, etc,
|
7
8
|
# in ./support/ and its subdirectories.
|
8
9
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
10
|
|
10
11
|
RSpec.configure do |config|
|
11
|
-
|
12
|
+
config.include DynportTools::HaveAttributesMatcher
|
13
|
+
config.after(:each) do
|
14
|
+
Timecop.return
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
|
-
|
18
|
+
def root
|
19
|
+
Pathname.new(File.expand_path("../../", __FILE__))
|
20
|
+
end
|
21
|
+
|
22
|
+
def redis_pidfile
|
23
|
+
root.join("tmp/redis.pid")
|
24
|
+
end
|
25
|
+
|
26
|
+
def redis_socket
|
27
|
+
root.join("tmp/redis.socket")
|
28
|
+
end
|
29
|
+
|
30
|
+
redis_config = [
|
31
|
+
"port 0",
|
32
|
+
"unixsocket #{redis_socket}",
|
33
|
+
"pidfile #{redis_pidfile}",
|
34
|
+
"daemonize yes"
|
35
|
+
].join("\n")
|
36
|
+
|
37
|
+
FileUtils.mkdir_p(File.dirname(redis_pidfile))
|
38
|
+
|
39
|
+
system("echo '#{redis_config}' | redis-server -")
|
40
|
+
|
41
|
+
at_exit do
|
42
|
+
pid = File.read(redis_pidfile).strip
|
43
|
+
system("kill #{pid}")
|
44
|
+
FileUtils.rm_f(redis_socket)
|
45
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynport_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 8
|
10
|
+
version: 0.2.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tobias Schwab
|
@@ -15,12 +15,13 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-08-
|
18
|
+
date: 2011-08-23 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
23
|
-
|
22
|
+
name: nokogiri
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
25
|
none: false
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
@@ -29,12 +30,12 @@ dependencies:
|
|
29
30
|
segments:
|
30
31
|
- 0
|
31
32
|
version: "0"
|
32
|
-
name: nokogiri
|
33
|
-
version_requirements: *id001
|
34
|
-
prerelease: false
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
33
|
type: :runtime
|
37
|
-
requirement:
|
34
|
+
requirement: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: redis
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
39
|
none: false
|
39
40
|
requirements:
|
40
41
|
- - ">="
|
@@ -43,12 +44,12 @@ dependencies:
|
|
43
44
|
segments:
|
44
45
|
- 0
|
45
46
|
version: "0"
|
46
|
-
name: redis
|
47
|
-
version_requirements: *id002
|
48
|
-
prerelease: false
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
47
|
type: :runtime
|
51
|
-
requirement:
|
48
|
+
requirement: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: typhoeus
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
52
53
|
none: false
|
53
54
|
requirements:
|
54
55
|
- - ">="
|
@@ -57,12 +58,12 @@ dependencies:
|
|
57
58
|
segments:
|
58
59
|
- 0
|
59
60
|
version: "0"
|
60
|
-
|
61
|
-
|
62
|
-
prerelease: false
|
61
|
+
type: :runtime
|
62
|
+
requirement: *id003
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
|
-
|
65
|
-
|
64
|
+
name: rspec
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
66
67
|
none: false
|
67
68
|
requirements:
|
68
69
|
- - ~>
|
@@ -73,12 +74,12 @@ dependencies:
|
|
73
74
|
- 3
|
74
75
|
- 0
|
75
76
|
version: 2.3.0
|
76
|
-
name: rspec
|
77
|
-
version_requirements: *id004
|
78
|
-
prerelease: false
|
79
|
-
- !ruby/object:Gem::Dependency
|
80
77
|
type: :development
|
81
|
-
requirement:
|
78
|
+
requirement: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: bundler
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
82
83
|
none: false
|
83
84
|
requirements:
|
84
85
|
- - ~>
|
@@ -89,12 +90,12 @@ dependencies:
|
|
89
90
|
- 0
|
90
91
|
- 0
|
91
92
|
version: 1.0.0
|
92
|
-
name: bundler
|
93
|
-
version_requirements: *id005
|
94
|
-
prerelease: false
|
95
|
-
- !ruby/object:Gem::Dependency
|
96
93
|
type: :development
|
97
|
-
requirement:
|
94
|
+
requirement: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: jeweler
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
98
99
|
none: false
|
99
100
|
requirements:
|
100
101
|
- - ~>
|
@@ -105,12 +106,12 @@ dependencies:
|
|
105
106
|
- 6
|
106
107
|
- 4
|
107
108
|
version: 1.6.4
|
108
|
-
name: jeweler
|
109
|
-
version_requirements: *id006
|
110
|
-
prerelease: false
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
109
|
type: :development
|
113
|
-
requirement:
|
110
|
+
requirement: *id006
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rcov
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
114
115
|
none: false
|
115
116
|
requirements:
|
116
117
|
- - ">="
|
@@ -119,12 +120,12 @@ dependencies:
|
|
119
120
|
segments:
|
120
121
|
- 0
|
121
122
|
version: "0"
|
122
|
-
name: rcov
|
123
|
-
version_requirements: *id007
|
124
|
-
prerelease: false
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
123
|
type: :development
|
127
|
-
requirement:
|
124
|
+
requirement: *id007
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: ZenTest
|
127
|
+
prerelease: false
|
128
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
128
129
|
none: false
|
129
130
|
requirements:
|
130
131
|
- - "="
|
@@ -135,12 +136,12 @@ dependencies:
|
|
135
136
|
- 5
|
136
137
|
- 0
|
137
138
|
version: 4.5.0
|
138
|
-
name: ZenTest
|
139
|
-
version_requirements: *id008
|
140
|
-
prerelease: false
|
141
|
-
- !ruby/object:Gem::Dependency
|
142
139
|
type: :development
|
143
|
-
requirement:
|
140
|
+
requirement: *id008
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: autotest
|
143
|
+
prerelease: false
|
144
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
144
145
|
none: false
|
145
146
|
requirements:
|
146
147
|
- - ">="
|
@@ -149,12 +150,12 @@ dependencies:
|
|
149
150
|
segments:
|
150
151
|
- 0
|
151
152
|
version: "0"
|
152
|
-
name: autotest
|
153
|
-
version_requirements: *id009
|
154
|
-
prerelease: false
|
155
|
-
- !ruby/object:Gem::Dependency
|
156
153
|
type: :development
|
157
|
-
requirement:
|
154
|
+
requirement: *id009
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: autotest-growl
|
157
|
+
prerelease: false
|
158
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
158
159
|
none: false
|
159
160
|
requirements:
|
160
161
|
- - ">="
|
@@ -163,12 +164,12 @@ dependencies:
|
|
163
164
|
segments:
|
164
165
|
- 0
|
165
166
|
version: "0"
|
166
|
-
name: autotest-growl
|
167
|
-
version_requirements: *id010
|
168
|
-
prerelease: false
|
169
|
-
- !ruby/object:Gem::Dependency
|
170
167
|
type: :development
|
171
|
-
requirement:
|
168
|
+
requirement: *id010
|
169
|
+
- !ruby/object:Gem::Dependency
|
170
|
+
name: ruby-debug
|
171
|
+
prerelease: false
|
172
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
172
173
|
none: false
|
173
174
|
requirements:
|
174
175
|
- - ">="
|
@@ -177,9 +178,22 @@ dependencies:
|
|
177
178
|
segments:
|
178
179
|
- 0
|
179
180
|
version: "0"
|
180
|
-
|
181
|
-
|
181
|
+
type: :development
|
182
|
+
requirement: *id011
|
183
|
+
- !ruby/object:Gem::Dependency
|
184
|
+
name: timecop
|
182
185
|
prerelease: false
|
186
|
+
version_requirements: &id012 !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
hash: 3
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
version: "0"
|
195
|
+
type: :development
|
196
|
+
requirement: *id012
|
183
197
|
description: Collection of various tools
|
184
198
|
email: tobias.schwab@dynport.de
|
185
199
|
executables:
|
@@ -191,6 +205,7 @@ extra_rdoc_files:
|
|
191
205
|
- LICENSE.txt
|
192
206
|
- README.rdoc
|
193
207
|
files:
|
208
|
+
- .autotest
|
194
209
|
- .document
|
195
210
|
- .rbenv-version
|
196
211
|
- .rspec
|
@@ -207,15 +222,19 @@ files:
|
|
207
222
|
- lib/dynport_tools.rb
|
208
223
|
- lib/dynport_tools/deep_merger.rb
|
209
224
|
- lib/dynport_tools/differ.rb
|
225
|
+
- lib/dynport_tools/eta.rb
|
210
226
|
- lib/dynport_tools/have_attributes.rb
|
211
227
|
- lib/dynport_tools/jenkins.rb
|
212
228
|
- lib/dynport_tools/redis_dumper.rb
|
229
|
+
- lib/dynport_tools/redis_q.rb
|
213
230
|
- lib/dynport_tools/xml_file.rb
|
214
231
|
- spec/dynport_tools/deep_merger_spec.rb
|
215
232
|
- spec/dynport_tools/differ_spec.rb
|
233
|
+
- spec/dynport_tools/eta_spec.rb
|
216
234
|
- spec/dynport_tools/have_attributes_spec.rb
|
217
235
|
- spec/dynport_tools/jenkins_spec.rb
|
218
236
|
- spec/dynport_tools/redis_dumper_spec.rb
|
237
|
+
- spec/dynport_tools/redis_q_spec.rb
|
219
238
|
- spec/dynport_tools/xml_file_spec.rb
|
220
239
|
- spec/dynport_tools_spec.rb
|
221
240
|
- spec/fixtures/file_a.xml
|