battman 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,232 @@
1
+ require 'battman/acpi_battery'
2
+
3
+ module Battman
4
+
5
+ describe AcpiBattery do
6
+ it 'is a subclass of Battery' do
7
+ expect(AcpiBattery.new).to be_a Battery
8
+ end
9
+
10
+ describe '.new' do
11
+ it 'accepts an precision option and sets the corresponding instance variable' do
12
+ battery = AcpiBattery.new(0, precision: 100)
13
+
14
+ expect(battery.instance_variable_get(:@precision)).to eq 100
15
+ end
16
+
17
+ it 'sets precision to 1000 by default' do
18
+ expect(AcpiBattery.new.instance_variable_get(:@precision)).to eq 1000
19
+ end
20
+ end
21
+
22
+ describe '#path' do
23
+
24
+ let(:path_prefix) { '/sys/class/power_supply' }
25
+
26
+ it 'builds the path for a smapi battery with the correct index' do
27
+ battery = AcpiBattery.new
28
+
29
+ expect(battery.path).to eq File.join(path_prefix, 'BAT0')
30
+
31
+ battery = AcpiBattery.new(1)
32
+
33
+ expect(battery.path).to eq File.join(path_prefix, 'BAT1')
34
+ end
35
+
36
+ it 'sets the corresponding instance variable' do
37
+ battery = AcpiBattery.new
38
+ path = battery.path
39
+
40
+ expect(battery.instance_variable_get(:@path)).to eq path
41
+ end
42
+ end
43
+
44
+ let(:battery) { AcpiBattery.new }
45
+
46
+ before(:each) do
47
+ allow(File).to receive(:join).and_call_original
48
+ end
49
+
50
+ describe '#remaining_percent' do
51
+
52
+ let(:energy_full_file) { File.join(battery.path, 'energy_full') }
53
+ let(:energy_now_file) { File.join(battery.path, 'energy_now') }
54
+
55
+ before(:each) do
56
+ allow(File).to receive(:read).with(energy_full_file).and_return("1000\n")
57
+ allow(File).to receive(:read).with(energy_now_file).and_return("100\n")
58
+ end
59
+
60
+ it 'reads the value from the correct files' do
61
+ battery.remaining_percent
62
+
63
+ expect(File).to have_received(:read).with(energy_full_file)
64
+ expect(File).to have_received(:read).with(energy_now_file)
65
+ end
66
+
67
+ it 'calculates the percentage from the read file contents' do
68
+ expect(battery.remaining_percent).to eq 10
69
+ end
70
+
71
+ end
72
+
73
+ describe '#power' do
74
+
75
+ let(:power_file) { File.join(battery.path, 'power_now') }
76
+
77
+ it 'reads the value from the correct file' do
78
+ allow(File).to receive(:read).with(power_file).and_return("1000000\n")
79
+ allow(battery).to receive(:state).and_return(:charging)
80
+ battery.power
81
+
82
+ expect(File).to have_received(:read).with(power_file)
83
+ end
84
+
85
+ it 'returns the power used in watt' do
86
+ allow(File).to receive(:read).with(power_file).and_return("1000000\n")
87
+ allow(battery).to receive(:state).and_return(:charging)
88
+
89
+ expect(battery.power).to eq(1.0)
90
+ end
91
+
92
+ it 'returns a negative value if state is :discharging' do
93
+ allow(File).to receive(:read).with(power_file).and_return("1000000\n")
94
+ allow(battery).to receive(:state).and_return(:discharging)
95
+
96
+ expect(battery.power).to be < 0
97
+ end
98
+
99
+ it 'returns a positive value if state is either :charging or :idle' do
100
+ allow(File).to receive(:read).with(power_file).and_return("1000000\n")
101
+ allow(battery).to receive(:state).and_return(:charging)
102
+
103
+ expect(battery.power).to be >= 0
104
+
105
+ allow(battery).to receive(:state).and_return(:idle)
106
+
107
+ expect(battery.power).to be >= 0
108
+ end
109
+
110
+ end
111
+
112
+ describe "#state" do
113
+ let(:state_file) { File.join(battery.path, 'status') }
114
+
115
+ it 'reads the state from the correct file' do
116
+ allow(File).to receive(:read).with(state_file).and_return("Discharging\n")
117
+ battery.state
118
+
119
+ expect(File).to have_received(:read).with(state_file)
120
+ end
121
+
122
+ it 'returns the correct state as a symbol' do
123
+ allow(File).to receive(:read).with(state_file).and_return("Discharging\n")
124
+
125
+ expect(battery.state).to eq :discharging
126
+ end
127
+
128
+ it 'returns :idle if state read from file is Unknown' do
129
+ allow(File).to receive(:read).with(state_file).and_return("Unknown\n")
130
+
131
+ expect(battery.state).to eq :idle
132
+ end
133
+ end
134
+
135
+ describe "#remaining_energy" do
136
+ let(:energy_file) { File.join(battery.path, 'energy_now') }
137
+
138
+ it 'reads the value from the correct file' do
139
+ allow(File).to receive(:read).with(energy_file).and_return("100000000\n")
140
+ battery.remaining_energy
141
+
142
+ expect(File).to have_received(:read).with(energy_file)
143
+ end
144
+
145
+ it 'returns the remaining energy in Wh' do
146
+ allow(File).to receive(:read).with(energy_file).and_return("100000000\n")
147
+
148
+ expect(battery.remaining_energy).to eq 100.0
149
+ end
150
+ end
151
+
152
+ describe '#remaining_running_time' do
153
+ it 'calculates the value from the current power and remaining energy' do
154
+ allow(battery).to receive(:state).and_return(:discharging)
155
+ allow(battery).to receive(:power).and_return(10.0)
156
+ allow(battery).to receive(:remaining_energy).and_return(100.0)
157
+ battery.remaining_running_time
158
+
159
+ expect(battery).to have_received(:power)
160
+ expect(battery).to have_received(:remaining_energy)
161
+
162
+ expect(battery.remaining_running_time).to eq 600
163
+ end
164
+
165
+ it 'raises a WrongStateError if the battery is not discharging' do
166
+ allow(battery).to receive(:power).and_return(10.0)
167
+ allow(battery).to receive(:remaining_energy).and_return(100.0)
168
+
169
+ allow(battery).to receive(:state).and_return(:discharging)
170
+ battery.remaining_running_time
171
+
172
+ allow(battery).to receive(:state).and_return(:idle)
173
+
174
+ expect { battery.remaining_running_time }.to raise_error(WrongStateError)
175
+
176
+ allow(battery).to receive(:state).and_return(:charging)
177
+
178
+ expect { battery.remaining_running_time }.to raise_error(WrongStateError)
179
+ end
180
+
181
+ end
182
+
183
+ describe "#full_energy" do
184
+ let(:energy_file) { File.join(battery.path, 'energy_full') }
185
+
186
+ it 'reads the value from the correct file' do
187
+ allow(File).to receive(:read).with(energy_file).and_return("100000000\n")
188
+ battery.full_energy
189
+
190
+ expect(File).to have_received(:read).with(energy_file)
191
+ end
192
+
193
+ it 'returns the remaining energy in Wh' do
194
+ allow(File).to receive(:read).with(energy_file).and_return("100000000\n")
195
+
196
+ expect(battery.full_energy).to eq 100.0
197
+ end
198
+ end
199
+
200
+ describe '#remaining_charging_time' do
201
+ it 'calculates the value from the current power and remaining energy' do
202
+ allow(battery).to receive(:state).and_return(:charging)
203
+ allow(battery).to receive(:power).and_return(10.0)
204
+ allow(battery).to receive(:remaining_energy).and_return(90.0)
205
+ allow(battery).to receive(:full_energy).and_return(100.0)
206
+ battery.remaining_charging_time
207
+
208
+ expect(battery).to have_received(:power)
209
+ expect(battery).to have_received(:remaining_energy)
210
+
211
+ expect(battery.remaining_charging_time).to eq 60.0
212
+ end
213
+
214
+ it 'raises a WrongStateError if the battery is not charging' do
215
+ allow(battery).to receive(:remaining_energy).and_return(90.0)
216
+ allow(battery).to receive(:power).and_return(10.0)
217
+ allow(battery).to receive(:full_energy).and_return(100.0)
218
+
219
+ allow(battery).to receive(:state).and_return(:charging)
220
+ battery.remaining_charging_time
221
+
222
+ allow(battery).to receive(:state).and_return(:discharging)
223
+
224
+ expect { battery.remaining_charging_time }.to raise_error(WrongStateError)
225
+
226
+ allow(battery).to receive(:state).and_return(:idle)
227
+
228
+ expect { battery.remaining_charging_time }.to raise_error(WrongStateError)
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,111 @@
1
+ require 'battman/battery'
2
+ module Battman
3
+ describe Battery do
4
+
5
+ describe '.new' do
6
+
7
+ it 'throws an AbstractError if directly instantiated' do
8
+ expect { Battery.new }.to raise_error AbstractError
9
+ end
10
+
11
+ let(:subclass) { Class.new(Battery) }
12
+
13
+ it 'allows instantiation of subclasses' do
14
+ subclass.new
15
+ end
16
+
17
+ it 'accepts an index and sets the corresponding instance variable' do
18
+ battery = subclass.new(1)
19
+
20
+ expect(battery.instance_variable_get(:@index)).to eq 1
21
+ end
22
+
23
+ it 'sets index to 0 by default' do
24
+ battery = subclass.new
25
+
26
+ expect(battery.instance_variable_get(:@index)).to eq 0
27
+ end
28
+
29
+ end
30
+
31
+ let(:battery) { Class.new(Battery).new }
32
+
33
+ [:remaining_percent, :power, :remaining_running_time,
34
+ :remaining_charging_time, :state, :remaining_energy,
35
+ :full_energy].each do |method|
36
+
37
+ describe "##{method}" do
38
+
39
+ it 'throws an NotImplementedError' do
40
+ expect { battery.send(method) }.to raise_error NotImplementedError
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ describe '#power_in' do
47
+ it 'requires a unit argument' do
48
+ expect { battery.power_in }.to raise_error(ArgumentError)
49
+ end
50
+
51
+ it 'only accepts units from CONVERSIONS[:power]' do
52
+ expect { battery.power_in(:unsupported_unit) }.to raise_error(UnsupportedUnitError)
53
+
54
+ allow(battery).to receive(:power).and_return(10.0)
55
+ battery.power_in(Battery::CONVERSIONS[:power].keys.sample)
56
+ end
57
+
58
+ it 'converts the power from watts to the given unit' do
59
+ allow(battery).to receive(:power).and_return(10.0)
60
+
61
+ expect(battery.power_in(:watts)).to eq 10.0
62
+ expect(battery.power_in(:milliwatts)).to eq 10_000.0
63
+ end
64
+ end
65
+
66
+ [:remaining_running_time, :remaining_charging_time].each do |method|
67
+ describe "#{method}_in" do
68
+ it 'requires a unit argument' do
69
+ expect { battery.send(:"#{method}_in") }.to raise_error(ArgumentError)
70
+ end
71
+
72
+ it 'only accepts units from CONVERSIONS[:time]' do
73
+ expect { battery.send(:"#{method}_in", :unsupported_unit) }.to raise_error(UnsupportedUnitError)
74
+
75
+ allow(battery).to receive(method).and_return(10.0)
76
+ battery.send(:"#{method}_in", Battery::CONVERSIONS[:time].keys.sample)
77
+ end
78
+
79
+ it 'converts the time from seconds to the given unit' do
80
+ allow(battery).to receive(method).and_return(3600.0)
81
+
82
+ expect(battery.send(:"#{method}_in", :seconds)).to eq 3600.0
83
+ expect(battery.send(:"#{method}_in", :minutes)).to eq 60.0
84
+ expect(battery.send(:"#{method}_in", :hours)).to eq 1.0
85
+ end
86
+ end
87
+ end
88
+
89
+ [:remaining_energy, :full_energy].each do |method|
90
+ describe "#{method}_in" do
91
+ it 'requires a unit argument' do
92
+ expect { battery.send(:"#{method}_in") }.to raise_error(ArgumentError)
93
+ end
94
+
95
+ it 'only accepts units from CONVERSIONS[:energy]' do
96
+ expect { battery.send(:"#{method}_in", :unsupported_unit) }.to raise_error(UnsupportedUnitError)
97
+
98
+ allow(battery).to receive(method).and_return(10.0)
99
+ battery.send(:"#{method}_in", Battery::CONVERSIONS[:energy].keys.sample)
100
+ end
101
+
102
+ it 'converts the time from watt hours to the given unit' do
103
+ allow(battery).to receive(method).and_return(10.0)
104
+
105
+ expect(battery.send(:"#{method}_in", :watt_hours)).to eq 10.0
106
+ expect(battery.send(:"#{method}_in", :milliwatt_hours)).to eq 10_000.0
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,62 @@
1
+ require 'battman/dsl/every_block'
2
+
3
+ module Battman
4
+ module DSL
5
+ describe EveryBlock do
6
+
7
+ describe '#check' do
8
+
9
+ let(:dsl) { double('DSL') }
10
+ let(:battery) { double('Battery') }
11
+ let(:state) { EveryBlock.new(dsl, battery, 10) }
12
+
13
+ before(:each) do
14
+ allow(battery).to receive(:power_in).with(:milliwatts)
15
+ allow(battery).to receive(:remaining_percent)
16
+ allow(dsl).to receive(:register)
17
+ end
18
+
19
+ it 'expects a battery attribute and a block' do
20
+ expect { state.check }.to raise_error(ArgumentError)
21
+ expect { state.check(:remaining_percent) }.to raise_error(ArgumentError)
22
+ state.check(:remaining_percent) {}
23
+ end
24
+
25
+ it 'allows additional arguments to pass methods' do
26
+ state.check(:power_in, :milliwatts) {}
27
+ end
28
+
29
+ it 'correctly registers a block on the dsl instance' do
30
+ task_was_called = false
31
+ value_passed = nil
32
+ last_value_passed = nil
33
+ task = Proc.new do |value, last_value|
34
+ task_was_called = true
35
+ value_passed = value
36
+ last_value_passed = last_value
37
+ end
38
+
39
+ expect(dsl).to receive(:register) do |interval, block|
40
+ expect(interval).to eq 10
41
+
42
+ expect(battery).to receive(:power_in).with(:milliwatts).and_return(10_000.0)
43
+ block.call
44
+
45
+ expect(task_was_called)
46
+ expect(value_passed).to eq 10_000.0
47
+ expect(last_value_passed).to be nil
48
+
49
+ expect(battery).to receive(:power_in).with(:milliwatts).and_return(9_000.0)
50
+ block.call
51
+
52
+ expect(value_passed).to eq 9_000.0
53
+ expect(last_value_passed).to eq 10_000.0
54
+ end
55
+
56
+ state.check(:power_in, :milliwatts, &task)
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ require 'battman/dsl/watch_block'
2
+
3
+ module Battman
4
+ module DSL
5
+ describe WatchBlock do
6
+ let(:dsl) { double('DSL') }
7
+ let(:battery) { double('Battery') }
8
+ let(:watcher) { WatchBlock.new(dsl, battery) }
9
+
10
+ describe '#every' do
11
+ it 'expects an interval in seconds and a block' do
12
+ expect { watcher.every }.to raise_error(ArgumentError)
13
+ expect { watcher.every(10) }.to raise_error(ArgumentError)
14
+
15
+ watcher.every(10) {}
16
+ end
17
+
18
+ it 'yields an object that responds to check' do
19
+ expect {|b| watcher.every(10, &b) }.to yield_with_args do |arg|
20
+ expect(arg).to respond_to(:check)
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,137 @@
1
+ require 'battman/dsl'
2
+ require 'battman/smapi_battery'
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ module Battman
6
+ describe DSL do
7
+
8
+ let(:dsl) { Class.new { include DSL }.new }
9
+
10
+ describe '.new' do
11
+ it 'initializes @blocks correctly' do
12
+ blocks = dsl.instance_variable_get(:@blocks)
13
+ expect(blocks).not_to be nil
14
+ expect(blocks[2]).to eq []
15
+
16
+ blocks[4] << :foo
17
+ blocks[4] << :bar
18
+ expect(blocks[4]).to eq [:foo, :bar]
19
+ end
20
+
21
+ it 'initializes @intervals_due correctly' do
22
+ intervals_due = dsl.instance_variable_get(:@intervals_due)
23
+
24
+ expect(intervals_due[1]).to eq 0
25
+ expect(intervals_due[:anything]).to eq 0
26
+ end
27
+
28
+ it 'yields self if given a block' do
29
+ expect {|b| Class.new { include DSL }.new(&b) }.to yield_with_args(DSL)
30
+ end
31
+ end
32
+
33
+ describe '#watch' do
34
+
35
+ it 'instantiates a battery of given type' do
36
+ expect(SmapiBattery).to receive(:new)
37
+
38
+ dsl.watch(:smapi) {}
39
+ end
40
+
41
+ it 'expects a valid type and a block' do
42
+ expect { dsl.watch }.to raise_error(ArgumentError)
43
+ expect { dsl.watch(:type) }.to raise_error(ArgumentError)
44
+ expect { dsl.watch(:invalid_type) {} }.to raise_error(LoadError)
45
+
46
+ dsl.watch(:smapi) {}
47
+ end
48
+
49
+ it 'yields a WatchBlock' do
50
+ expect {|b| dsl.watch(:smapi, &b) }.to yield_with_args(DSL::WatchBlock)
51
+ end
52
+
53
+ end
54
+
55
+ describe '#register' do
56
+
57
+ it 'adds a given block to the blocks hash' do
58
+ first_block = Proc.new {}
59
+ second_block = Proc.new {}
60
+ dsl.register(1, first_block)
61
+ dsl.register(1, second_block)
62
+
63
+ expect(dsl.instance_variable_get(:@blocks)[1]).to eq [first_block, second_block]
64
+ end
65
+
66
+ it 'sets the greatest common interval correctly' do
67
+ dsl.register(4, Proc.new {})
68
+ expect(dsl.instance_variable_get(:@greatest_common_interval)).to eq 4
69
+
70
+ dsl.register(8, Proc.new {})
71
+ expect(dsl.instance_variable_get(:@greatest_common_interval)).to eq 4
72
+
73
+ dsl.register(10, Proc.new {})
74
+ expect(dsl.instance_variable_get(:@greatest_common_interval)).to eq 2
75
+ end
76
+
77
+ end
78
+
79
+ describe '#run_once' do
80
+ let(:blocks) do
81
+ blocks = Hash.new {|hash, key| hash[key] = []}
82
+ 3.times do |i|
83
+ block = double('proc')
84
+ allow(block).to receive(:call)
85
+ blocks[2**(i+1)] << block
86
+ end
87
+
88
+ blocks
89
+ end
90
+
91
+ let(:greatest_common_interval) { blocks.keys.inject(:gcd) }
92
+
93
+ before(:each) do
94
+ dsl.instance_variable_set(:@blocks, blocks)
95
+ dsl.instance_variable_set(:@greatest_common_interval, greatest_common_interval)
96
+ end
97
+
98
+ it 'calls all registered blocks on the first run' do
99
+ blocks.values.inject(&:+).each do |block|
100
+ expect(block).to receive(:call).once
101
+ end
102
+
103
+ dsl.run_once
104
+ end
105
+
106
+ it 'sets the correct schedules for blocks in @intervals_due' do
107
+ expected_schedule = {}
108
+ blocks.keys.each do |interval|
109
+ expected_schedule[interval] = interval
110
+ end
111
+
112
+ dsl.run_once
113
+ expect(dsl.instance_variable_get(:@intervals_due)).to eq expected_schedule
114
+ end
115
+
116
+ it 'calls only scheduled blocks on consecutive runs' do
117
+ dsl.run_once
118
+ dsl.run_once
119
+
120
+ blocks[greatest_common_interval].each do |block|
121
+ expect(block).to have_received(:call).twice
122
+ end
123
+
124
+ dsl.run_once
125
+
126
+ blocks[greatest_common_interval].each do |block|
127
+ expect(block).to have_received(:call).exactly(3).times
128
+ end
129
+
130
+ blocks[greatest_common_interval * 2].each do |block|
131
+ expect(block).to have_received(:call).twice
132
+ end
133
+ end
134
+ end
135
+
136
+ end
137
+ end