roxanne 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Configuration::YAML do
4
+
5
+ def spec_file(variation)
6
+ File.expand_path(File.join(File.dirname(__FILE__), '../../yaml', "#{variation}.yml"))
7
+ end
8
+
9
+ it 'uses activation default settings' do
10
+ subject.new(spec_file('defaults')).tap do |config|
11
+ config.active_days.must_equal 1..5
12
+ config.timerange.must_equal 8..19
13
+ end
14
+ end
15
+
16
+ it 'overrides activation settings' do
17
+ subject.new(spec_file('full_blown')).tap do |config|
18
+ config.active_days.must_equal 1..3
19
+ config.timerange.must_equal 9..18
20
+ end
21
+ end
22
+
23
+ module Roxanne::Spec
24
+ class Consumer
25
+ attr_accessor :consumer_opt
26
+ end
27
+
28
+ class Publisher
29
+ attr_accessor :publisher_opt
30
+ end
31
+ end
32
+
33
+ it 'create consumers' do
34
+ subject.new(spec_file('defaults')).tap do |config|
35
+ config.consumers.size.must_equal 1
36
+ config.consumers.first.must_be_instance_of Roxanne::Spec::Consumer
37
+ config.consumers.first.consumer_opt.must_equal "some-value"
38
+ end
39
+ end
40
+
41
+ it 'create publisher' do
42
+ subject.new(spec_file('defaults')).tap do |config|
43
+ config.publisher.must_be_instance_of Roxanne::Spec::Publisher
44
+ config.publisher.publisher_opt.must_equal "another-value"
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Configuration::Base do
4
+
5
+ after do
6
+ Timecop.return
7
+ end
8
+
9
+ it 'initializes settings' do
10
+ subject.new.tap do |config|
11
+ config.publisher.must_be_nil
12
+ config.consumers.must_equal []
13
+ end
14
+ end
15
+
16
+ it 'is active when in specified days and time range' do
17
+ Timecop.travel(Time.local(2014, 3, 7, 19, 59, 59))
18
+ subject.new.activated.must_equal true
19
+
20
+ Timecop.travel(Time.local(2014, 3, 3, 8, 0, 0))
21
+ subject.new.activated.must_equal true
22
+ end
23
+
24
+ it 'is not active when out of specified days' do
25
+ Timecop.travel(Time.local(2014, 3, 8, 8, 0, 0))
26
+ subject.new.activated.must_equal false
27
+ end
28
+
29
+ it 'is not active when out of time range' do
30
+ Timecop.travel(Time.local(2014, 3, 7, 20, 0, 0))
31
+ subject.new.activated.must_equal false
32
+
33
+ Timecop.travel(Time.local(2014, 3, 3, 7, 59, 59))
34
+ subject.new.activated.must_equal false
35
+ end
36
+
37
+ end
@@ -0,0 +1,54 @@
1
+ describe Roxanne::GPIO::Publisher do
2
+
3
+ before do
4
+ @publisher = subject.new
5
+ @publisher.green_pin = 17
6
+ @publisher.orange_pin = 25
7
+ @publisher.red_pin = 22
8
+ end
9
+
10
+ it 'disable gpio pins' do
11
+ expects_all_off
12
+ @publisher.disable
13
+ end
14
+
15
+ it 'sets all pins to zero on nil status' do
16
+ expects_all_off
17
+ @publisher.push nil, nil
18
+ end
19
+
20
+ it 'switch green pin to 1 on green status' do
21
+ expects_pin(@publisher.green_pin).expects(:value).with 1
22
+ expects_pin(@publisher.orange_pin).expects(:value).with 0
23
+ expects_pin(@publisher.red_pin).expects(:value).with 0
24
+ @publisher.push nil, :green
25
+ end
26
+
27
+ it 'switch red pin to 1 on red status' do
28
+ expects_pin(@publisher.green_pin).expects(:value).with 0
29
+ expects_pin(@publisher.orange_pin).expects(:value).with 0
30
+ expects_pin(@publisher.red_pin).expects(:value).with 1
31
+ @publisher.push nil, :red
32
+ end
33
+
34
+ it 'switch orange pin to 1 on orange status' do
35
+ expects_pin(@publisher.green_pin).expects(:value).with 0
36
+ expects_pin(@publisher.orange_pin).expects(:value).with 1
37
+ expects_pin(@publisher.red_pin).expects(:value).with 0
38
+ @publisher.push nil, :orange
39
+ end
40
+
41
+ def expects_pin(num)
42
+ mock.tap do |pin|
43
+ Taopaipai.gpio.expects(:pin).with(num, direction: :out).returns(pin)
44
+ end
45
+ end
46
+
47
+ def expects_all_off
48
+ [17, 25, 22].each do |num|
49
+ expects_pin(num).expects(:value).with(0)
50
+ end
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Jenkins::Consumer do
4
+
5
+ def jobs(*colors)
6
+ { 'jobs' => colors.map{|color| {'color'=>color} } }.to_json
7
+ end
8
+
9
+ it 'defaults to green' do
10
+ subject.new.handle_response(jobs).must_equal :green
11
+ end
12
+
13
+ it 'ignores unknown job colors' do
14
+ subject.new.handle_response(jobs('british_racing_green')).must_equal :green
15
+ end
16
+
17
+ it 'returns green if all good' do
18
+ subject.new.handle_response(jobs('blue', 'yellow')).must_equal :green
19
+ end
20
+
21
+ it 'returns orange if one of the jobs is orange' do
22
+ subject.new.handle_response(jobs('yellow_anime', 'blue', 'yellow')).must_equal :orange
23
+ subject.new.handle_response(jobs('blue_anime', 'blue', 'yellow')).must_equal :orange
24
+ subject.new.handle_response(jobs('red_anime', 'blue', 'yellow_anime')).must_equal :orange
25
+ end
26
+
27
+ it 'returns red if one of the jobs is read' do
28
+ subject.new.handle_response(jobs('red', 'blue', 'yellow_anime')).must_equal :red
29
+ end
30
+
31
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Loop do
4
+ before do
5
+ @publisher = mock
6
+ @config = Roxanne::Configuration::Base.new
7
+ @config.stubs(:publisher).returns @publisher
8
+ end
9
+
10
+ it 'disables publishers unless active' do
11
+ @config.expects(:activated).returns false
12
+ @publisher.expects(:disable)
13
+ subject.new(@config).cycle
14
+ end
15
+
16
+ describe 'activated' do
17
+
18
+ before do
19
+ @config.expects(:activated).at_least(1).returns true
20
+ @publisher.expects(:disable).never
21
+
22
+ 3.times do
23
+ @config.consumers << mock
24
+ end
25
+ end
26
+
27
+ it 'publishes green if all of consumers answer green' do
28
+ @config.consumers.each do |consumer|
29
+ consumer.expects(:pull).returns :green
30
+ end
31
+ @publisher.expects(:push).with(nil, :green)
32
+ subject.new(@config).cycle
33
+ end
34
+
35
+ it 'publishes red if one of the consumers answers red' do
36
+ @config.consumers.first.expects(:pull).returns :red
37
+ @publisher.expects(:push).with(nil, :red)
38
+ subject.new(@config).cycle
39
+ end
40
+
41
+ def expects_pulls(*statuses)
42
+ @config.consumers.each_with_index do |consumer, i|
43
+ consumer.expects(:pull).returns statuses[i] if statuses[i]
44
+ end
45
+ end
46
+
47
+ it 'publishes orange if a consumer answers orange and the previous status wasnt green' do
48
+ loop = subject.new(@config)
49
+ # First cycle to turn it red
50
+ expects_pulls(:red)
51
+ @publisher.expects(:push).with(nil, :red)
52
+ loop.cycle
53
+
54
+ # Second cycle to orange
55
+ expects_pulls(:green, :orange, :green)
56
+ @publisher.expects(:push).with(:red, :orange)
57
+ loop.cycle
58
+ end
59
+
60
+ it 'publishes green if orange and previous state wasnt red' do
61
+ loop = subject.new(@config)
62
+
63
+ expects_pulls(:green, :green, :green)
64
+ @publisher.expects(:push).with(nil, :green)
65
+ loop.cycle
66
+
67
+ expects_pulls(:orange, :green, :green)
68
+ @publisher.expects(:push).with(:green, :green)
69
+ loop.cycle
70
+ end
71
+
72
+ end
73
+
74
+ it 'publishes nil on reset' do
75
+ loop = subject.new(@config)
76
+ @publisher.expects(:push).with(nil, nil)
77
+ loop.reset
78
+ end
79
+
80
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Server do
4
+
5
+ describe 'with a test double' do
6
+ class ServerDouble < Roxanne::Server
7
+ attr_reader :slept_for, :looped
8
+
9
+ def initialize(configuration, opts)
10
+ @config = configuration
11
+ super(opts)
12
+ end
13
+
14
+ def loop(&block)
15
+ @looped = true
16
+ yield
17
+ end
18
+
19
+ def sleep(time)
20
+ @slept_for = time
21
+ end
22
+
23
+ def config(args)
24
+ @config
25
+ end
26
+ end
27
+
28
+ before do
29
+ @configuration = Roxanne::Configuration::Base.new
30
+ @server = ServerDouble.new(
31
+ @configuration,
32
+ working_dir: @working_dir = File.join(File.dirname(__FILE__), '../..')
33
+ )
34
+ end
35
+
36
+ it 'logs, cycle and sleep' do
37
+ STDOUT.expects(:puts).with("Roxanne starting in #{@working_dir}")
38
+ controller = Roxanne::Loop.new(@configuration)
39
+ Roxanne::Loop.expects(:new).with(@configuration).returns(controller)
40
+ controller.expects(:cycle)
41
+ @server.start([])
42
+ @server.looped.must_equal true
43
+ @server.slept_for.must_equal 5
44
+ end
45
+
46
+ it 'stops' do
47
+ @server.instance_variable_set(:@controller, controller = mock)
48
+ controller.expects(:reset)
49
+ @server.stop
50
+ end
51
+ end
52
+
53
+ it 'creates a YAML configuration using the default path' do
54
+ Roxanne::Configuration::YAML.expects(:new).with('config/config.yml').returns(config = mock)
55
+ subject.new({working_dir: ''}).config([]).must_equal config
56
+ end
57
+
58
+ it 'creates a YAML configuration using the provided path' do
59
+ Roxanne::Configuration::YAML.expects(:new).with('specific-config.yml').returns(config = mock)
60
+ subject.new({working_dir: ''}).config(['specific-config.yml']).must_equal config
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Roxanne::Travis::Consumer do
4
+
5
+ before do
6
+ @subject = subject.new
7
+ @subject.organization_or_user = 'servebox'
8
+ end
9
+
10
+ def expects_repositories_state( *repos )
11
+ mock_repos = repos.map do |repo|
12
+ mock.tap do |mock|
13
+ mock.stubs(:active?).returns(repo[:active].nil? ? true : repo[:active])
14
+ mock.stubs(:last_build_state).returns(repo[:build_state])
15
+ end
16
+ end
17
+ Travis::Repository.expects(:find_all).with(owner_name: 'servebox').returns(mock_repos)
18
+ end
19
+
20
+ it 'defaults to green' do
21
+ expects_repositories_state
22
+ @subject.pull.must_equal :green
23
+ end
24
+
25
+ it 'ignores inactive repositories' do
26
+ expects_repositories_state({ active: false, build_state: 'failed' })
27
+ @subject.pull.must_equal :green
28
+ end
29
+
30
+ it 'returns green if all good' do
31
+ expects_repositories_state({ build_state: 'passed' })
32
+ @subject.pull.must_equal :green
33
+ end
34
+
35
+ it 'returns orange if one of the jobs is orange' do
36
+ expects_repositories_state({ build_state: 'started' }, {build_state: 'passed'})
37
+ @subject.pull.must_equal :orange
38
+ end
39
+
40
+ it 'returns red if one of the jobs is read' do
41
+ expects_repositories_state({ build_state: 'failed' }, { build_state: 'started' },
42
+ {build_state: 'passed'})
43
+ @subject.pull.must_equal :red
44
+ end
45
+
46
+ end
@@ -0,0 +1,20 @@
1
+ #require 'simplecov'
2
+ #require 'coveralls'
3
+ #
4
+ #SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ # SimpleCov::Formatter::HTMLFormatter,
6
+ # Coveralls::SimpleCov::Formatter
7
+ #]
8
+ #
9
+ #SimpleCov.start do
10
+ # add_filter '/spec/'
11
+ #end
12
+
13
+ require 'roxanne'
14
+ require 'minitest'
15
+ require 'minitest/spec'
16
+ require 'minitest/autorun'
17
+ require 'minitest/pride'
18
+ require 'minitest-implicit-subject'
19
+ require 'timecop'
20
+ require 'mocha/setup'
@@ -0,0 +1,8 @@
1
+ roxanne:
2
+ consumers:
3
+ consumer_id:
4
+ class: "Roxanne::Spec::Consumer"
5
+ consumer_opt: "some-value"
6
+ publisher:
7
+ class: "Roxanne::Spec::Publisher"
8
+ publisher_opt: "another-value"
@@ -0,0 +1,12 @@
1
+ roxanne:
2
+ activation:
3
+ days: !ruby/range 1..3
4
+ # Active from ~ 08:00 to 18:59
5
+ timerange: !ruby/range 9..18
6
+ consumers:
7
+ consumer_id:
8
+ class: "Roxanne::Spec::Consumer"
9
+ consumer_opt: "some-value"
10
+ publisher:
11
+ class: "Roxanne::Spec::Publisher"
12
+ publisher_opt: "another-value"
metadata ADDED
@@ -0,0 +1,280 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roxanne
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jef Mathiot
8
+ - Patrice Izzo
9
+ - Fabrice Nourisson
10
+ - Benjamin Severac
11
+ - Eric Hartmann
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2014-04-03 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: json
19
+ requirement: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: daemon-spawn
33
+ requirement: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ - !ruby/object:Gem::Dependency
46
+ name: activesupport
47
+ requirement: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 3.0.0
52
+ type: :runtime
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 3.0.0
59
+ - !ruby/object:Gem::Dependency
60
+ name: taopaipai
61
+ requirement: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - "~>"
64
+ - !ruby/object:Gem::Version
65
+ version: 0.1.1
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: 0.1.1
73
+ - !ruby/object:Gem::Dependency
74
+ name: travis
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 1.6.8
80
+ type: :runtime
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: 1.6.8
87
+ - !ruby/object:Gem::Dependency
88
+ name: bundler
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '1.6'
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '1.6'
101
+ - !ruby/object:Gem::Dependency
102
+ name: rake
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ type: :development
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ - !ruby/object:Gem::Dependency
116
+ name: minitest
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: 5.0.7
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: 5.0.7
129
+ - !ruby/object:Gem::Dependency
130
+ name: minitest-implicit-subject
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: 1.4.0
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: 1.4.0
143
+ - !ruby/object:Gem::Dependency
144
+ name: rb-readline
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: 0.5.0
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: 0.5.0
157
+ - !ruby/object:Gem::Dependency
158
+ name: guard-minitest
159
+ requirement: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: 2.1.3
164
+ type: :development
165
+ prerelease: false
166
+ version_requirements: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: 2.1.3
171
+ - !ruby/object:Gem::Dependency
172
+ name: timecop
173
+ requirement: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ type: :development
179
+ prerelease: false
180
+ version_requirements: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ - !ruby/object:Gem::Dependency
186
+ name: mocha
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ type: :development
193
+ prerelease: false
194
+ version_requirements: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ description: 'Roxanne: publish your CI status to your device of choice'
200
+ email:
201
+ - foss@servebox.com
202
+ executables:
203
+ - roxanne
204
+ extensions: []
205
+ extra_rdoc_files: []
206
+ files:
207
+ - ".gitignore"
208
+ - ".ruby-version"
209
+ - ".travis.yml"
210
+ - Gemfile
211
+ - Guardfile
212
+ - LICENSE.txt
213
+ - README.md
214
+ - Rakefile
215
+ - bin/roxanne
216
+ - config/config.yml
217
+ - lib/roxanne.rb
218
+ - lib/roxanne/configuration.rb
219
+ - lib/roxanne/configuration/yaml.rb
220
+ - lib/roxanne/consumers.rb
221
+ - lib/roxanne/gpio/publisher.rb
222
+ - lib/roxanne/http/consumer.rb
223
+ - lib/roxanne/http_support.rb
224
+ - lib/roxanne/jenkins/consumer.rb
225
+ - lib/roxanne/loop.rb
226
+ - lib/roxanne/publishers.rb
227
+ - lib/roxanne/server.rb
228
+ - lib/roxanne/test/consumer.rb
229
+ - lib/roxanne/test/publisher.rb
230
+ - lib/roxanne/travis/consumer.rb
231
+ - lib/roxanne/version.rb
232
+ - roxanne.gemspec
233
+ - service/etc_init.d_roxanne
234
+ - service/roxanne.sh
235
+ - spec/roxanne/configuration/yaml_spec.rb
236
+ - spec/roxanne/configuration_spec.rb
237
+ - spec/roxanne/gpio/publisher_spec.rb
238
+ - spec/roxanne/jenkins/consumer_spec.rb
239
+ - spec/roxanne/loop_spec.rb
240
+ - spec/roxanne/server_spec.rb
241
+ - spec/roxanne/travis/consumer_spec.rb
242
+ - spec/spec_helper.rb
243
+ - spec/yaml/defaults.yml
244
+ - spec/yaml/full_blown.yml
245
+ homepage: http://github.com/servebox/roxanne
246
+ licenses:
247
+ - MIT
248
+ metadata: {}
249
+ post_install_message:
250
+ rdoc_options: []
251
+ require_paths:
252
+ - lib
253
+ required_ruby_version: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ required_rubygems_version: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - ">="
261
+ - !ruby/object:Gem::Version
262
+ version: '0'
263
+ requirements: []
264
+ rubyforge_project:
265
+ rubygems_version: 2.2.2
266
+ signing_key:
267
+ specification_version: 4
268
+ summary: Aggregate the status of Continuous Integration jobs or other sources and
269
+ publish them
270
+ test_files:
271
+ - spec/roxanne/configuration/yaml_spec.rb
272
+ - spec/roxanne/configuration_spec.rb
273
+ - spec/roxanne/gpio/publisher_spec.rb
274
+ - spec/roxanne/jenkins/consumer_spec.rb
275
+ - spec/roxanne/loop_spec.rb
276
+ - spec/roxanne/server_spec.rb
277
+ - spec/roxanne/travis/consumer_spec.rb
278
+ - spec/spec_helper.rb
279
+ - spec/yaml/defaults.yml
280
+ - spec/yaml/full_blown.yml