pulse-meter-client-backport 0.1.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/.gitignore +19 -0
- data/.rbenv-version +1 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/Procfile +3 -0
- data/README.md +98 -0
- data/Rakefile +53 -0
- data/bin/pulse +6 -0
- data/examples/readme_client_example.rb +52 -0
- data/lib/pulse-meter.rb +17 -0
- data/lib/pulse-meter/mixins/dumper.rb +76 -0
- data/lib/pulse-meter/mixins/utils.rb +93 -0
- data/lib/pulse-meter/sensor.rb +44 -0
- data/lib/pulse-meter/sensor/base.rb +75 -0
- data/lib/pulse-meter/sensor/counter.rb +36 -0
- data/lib/pulse-meter/sensor/hashed_counter.rb +31 -0
- data/lib/pulse-meter/sensor/indicator.rb +33 -0
- data/lib/pulse-meter/sensor/timeline.rb +180 -0
- data/lib/pulse-meter/sensor/timelined/average.rb +26 -0
- data/lib/pulse-meter/sensor/timelined/counter.rb +16 -0
- data/lib/pulse-meter/sensor/timelined/hashed_counter.rb +22 -0
- data/lib/pulse-meter/sensor/timelined/max.rb +25 -0
- data/lib/pulse-meter/sensor/timelined/median.rb +14 -0
- data/lib/pulse-meter/sensor/timelined/min.rb +25 -0
- data/lib/pulse-meter/sensor/timelined/percentile.rb +31 -0
- data/lib/pulse-meter/version.rb +3 -0
- data/pulse-meter-client-backport.gemspec +33 -0
- data/spec/pulse_meter/mixins/dumper_spec.rb +141 -0
- data/spec/pulse_meter/mixins/utils_spec.rb +125 -0
- data/spec/pulse_meter/sensor/base_spec.rb +97 -0
- data/spec/pulse_meter/sensor/counter_spec.rb +54 -0
- data/spec/pulse_meter/sensor/hashed_counter_spec.rb +39 -0
- data/spec/pulse_meter/sensor/indicator_spec.rb +43 -0
- data/spec/pulse_meter/sensor/timeline_spec.rb +45 -0
- data/spec/pulse_meter/sensor/timelined/average_spec.rb +6 -0
- data/spec/pulse_meter/sensor/timelined/counter_spec.rb +6 -0
- data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +8 -0
- data/spec/pulse_meter/sensor/timelined/max_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/median_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/min_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +17 -0
- data/spec/pulse_meter_spec.rb +16 -0
- data/spec/shared_examples/timeline_sensor.rb +274 -0
- data/spec/shared_examples/timelined_subclass.rb +23 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/matchers.rb +45 -0
- metadata +276 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Counter do
|
4
|
+
let(:name){ :some_counter }
|
5
|
+
let(:sensor){ described_class.new(name) }
|
6
|
+
let(:redis){ PulseMeter.redis }
|
7
|
+
|
8
|
+
describe "#event" do
|
9
|
+
it "should increment sensor value by passed value" do
|
10
|
+
expect{ sensor.event(10) }.to change{ sensor.value }.from(0).to(10)
|
11
|
+
expect{ sensor.event(15) }.to change{ sensor.value }.from(10).to(25)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should truncate increment value" do
|
15
|
+
expect{ sensor.event(10.4) }.to change{ sensor.value }.from(0).to(10)
|
16
|
+
expect{ sensor.event(15.1) }.to change{ sensor.value }.from(10).to(25)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#value_key" do
|
21
|
+
it "should be composed of sensor name and pulse_meter:value: prefix" do
|
22
|
+
sensor.value_key.should == "pulse_meter:value:#{name}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#value" do
|
27
|
+
it "should have initial value 0" do
|
28
|
+
sensor.value.should == 0
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should store stringified value by value_key" do
|
32
|
+
sensor.event(123)
|
33
|
+
sensor.value.should == 123
|
34
|
+
redis.get(sensor.value_key).should == '123'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#incr" do
|
39
|
+
it "should increment value by 1" do
|
40
|
+
expect{ sensor.incr }.to change{ sensor.value }.from(0).to(1)
|
41
|
+
expect{ sensor.incr }.to change{ sensor.value }.from(1).to(2)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#cleanup" do
|
46
|
+
it "should remove all sensor data" do
|
47
|
+
sensor.annotate("My Counter")
|
48
|
+
sensor.event(123)
|
49
|
+
sensor.cleanup
|
50
|
+
redis.keys('*').should be_empty
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::HashedCounter do
|
4
|
+
let(:name){ :some_counter }
|
5
|
+
let(:sensor){ described_class.new(name) }
|
6
|
+
let(:redis){ PulseMeter.redis }
|
7
|
+
|
8
|
+
describe "#event" do
|
9
|
+
it "should increment sensor value by passed value" do
|
10
|
+
expect{ sensor.event({"foo" => 10}) }.to change{ sensor.value["foo"] }.from(0).to(10)
|
11
|
+
expect{ sensor.event({"foo" => 15}) }.to change{ sensor.value["foo"] }.from(10).to(25)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should truncate increment value" do
|
15
|
+
expect{ sensor.event({"foo" => 10.4}) }.to change{ sensor.value["foo"] }.from(0).to(10)
|
16
|
+
expect{ sensor.event({"foo" => 15.1}) }.to change{ sensor.value["foo"] }.from(10).to(25)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#value" do
|
21
|
+
it "should have initial value 0" do
|
22
|
+
sensor.value["foo"].should == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should store redis hash by value_key" do
|
26
|
+
sensor.event({"foo" => 1})
|
27
|
+
sensor.value.should == {"foo" => 1}
|
28
|
+
redis.hgetall(sensor.value_key).should == {"foo" => "1"}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#incr" do
|
33
|
+
it "should increment key value by 1" do
|
34
|
+
expect{ sensor.incr("foo") }.to change{ sensor.value["foo"] }.from(0).to(1)
|
35
|
+
expect{ sensor.incr("foo") }.to change{ sensor.value["foo"] }.from(1).to(2)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Indicator do
|
4
|
+
let(:name){ :some_value }
|
5
|
+
let(:sensor){ described_class.new(name) }
|
6
|
+
let(:redis){ PulseMeter.redis }
|
7
|
+
|
8
|
+
describe "#event" do
|
9
|
+
it "should set sensor value to passed value" do
|
10
|
+
expect{ sensor.event(10.4) }.to change{ sensor.value }.from(0).to(10.4)
|
11
|
+
expect{ sensor.event(15.1) }.to change{ sensor.value }.from(10.4).to(15.1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#value_key" do
|
16
|
+
it "should be composed of sensor name and pulse_meter:value: prefix" do
|
17
|
+
sensor.value_key.should == "pulse_meter:value:#{name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#value" do
|
22
|
+
it "should have initial value 0" do
|
23
|
+
sensor.value.should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should store stringified value by value_key" do
|
27
|
+
sensor.event(123)
|
28
|
+
sensor.value.should == 123
|
29
|
+
redis.get(sensor.value_key) == '123'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#cleanup" do
|
34
|
+
it "should remove all sensor data" do
|
35
|
+
sensor.annotate("My Indicator")
|
36
|
+
sensor.event(123)
|
37
|
+
sensor.cleanup
|
38
|
+
redis.keys('*').should be_empty
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Timeline do
|
4
|
+
let(:name){ :some_value_with_history }
|
5
|
+
let(:ttl){ 100 }
|
6
|
+
let(:raw_data_ttl){ 10 }
|
7
|
+
let(:interval){ 5 }
|
8
|
+
let(:reduce_delay){ 3 }
|
9
|
+
let(:good_init_values){ {:ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay} }
|
10
|
+
let(:sensor){ described_class.new(name, good_init_values) }
|
11
|
+
let(:redis){ PulseMeter.redis }
|
12
|
+
|
13
|
+
it_should_behave_like "timeline sensor"
|
14
|
+
|
15
|
+
describe '#new' do
|
16
|
+
INIT_VALUE_NAMES = {
|
17
|
+
:with_defaults => [:raw_data_ttl, :reduce_delay],
|
18
|
+
:without_defaults => [:ttl, :interval]
|
19
|
+
}
|
20
|
+
|
21
|
+
it "should initialize #ttl #raw_data_ttl #interval and #name attributes" do
|
22
|
+
sensor.name.should == name.to_s
|
23
|
+
|
24
|
+
sensor.ttl.should == ttl
|
25
|
+
sensor.raw_data_ttl.should == raw_data_ttl
|
26
|
+
sensor.interval.should == interval
|
27
|
+
end
|
28
|
+
|
29
|
+
INIT_VALUE_NAMES[:with_defaults].each do |value|
|
30
|
+
it "should not raise exception if #{value.inspect} is not defined" do
|
31
|
+
values = good_init_values
|
32
|
+
values.delete(value)
|
33
|
+
expect {described_class.new(name, good_init_values)}.not_to raise_exception(ArgumentError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should assign default value to #{value.inspect} if it is not defined" do
|
37
|
+
values = good_init_values
|
38
|
+
values.delete(value)
|
39
|
+
obj = described_class.new(name, good_init_values)
|
40
|
+
obj.send(value).should be_kind_of(Fixnum)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Timelined::HashedCounter do
|
4
|
+
it_should_behave_like "timeline sensor", {}, {:foo => 1}
|
5
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => 2}], {:foo => 3}.to_json
|
6
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => :bad_value}], {:foo => 1}.to_json
|
7
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:boo => 2}], {:foo => 1, :boo => 2}.to_json
|
8
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Timelined::Percentile do
|
4
|
+
it_should_behave_like "timeline sensor", {:p => 0.8}
|
5
|
+
it_should_behave_like "timelined subclass", [5, 4, 2, 2, 2, 2, 2, 2, 2, 1], 2, {:p => 0.8}
|
6
|
+
it_should_behave_like "timelined subclass", [1], 1, {:p => 0.8}
|
7
|
+
|
8
|
+
let(:init_values) {{:ttl => 1, :raw_data_ttl => 1, :interval => 1, :reduce_delay => 1}}
|
9
|
+
let(:name) {"percentile"}
|
10
|
+
|
11
|
+
it "should raise exception when percentile is not between 0 and 1" do
|
12
|
+
expect {described_class.new(name, init_values.merge({:p => -1}))}.to raise_exception(ArgumentError)
|
13
|
+
expect {described_class.new(name, init_values.merge({:p => 1.1}))}.to raise_exception(ArgumentError)
|
14
|
+
expect {described_class.new(name, init_values.merge({:p => 0.1}))}.not_to raise_exception(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter do
|
4
|
+
describe "::redis=" do
|
5
|
+
it "should store redis" do
|
6
|
+
PulseMeter.redis = 'redis'
|
7
|
+
PulseMeter.redis.should == 'redis'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
describe "::redis" do
|
11
|
+
it "should retrieve redis" do
|
12
|
+
PulseMeter.redis = 'redis'
|
13
|
+
PulseMeter.redis.should == 'redis'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
shared_examples_for "timeline sensor" do |extra_init_values, default_event|
|
2
|
+
class Dummy
|
3
|
+
include PulseMeter::Mixins::Dumper
|
4
|
+
def name; :dummy end
|
5
|
+
def redis; PulseMeter.redis; end
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:name){ :some_value_with_history }
|
9
|
+
let(:ttl){ 100 }
|
10
|
+
let(:raw_data_ttl){ 30 }
|
11
|
+
let(:interval){ 5 }
|
12
|
+
let(:reduce_delay){ 3 }
|
13
|
+
let(:good_init_values){ {:ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay}.merge(extra_init_values || {}) }
|
14
|
+
let!(:sensor){ described_class.new(name, good_init_values) }
|
15
|
+
let(:dummy) {Dummy.new}
|
16
|
+
let(:base_class){ PulseMeter::Sensor::Base }
|
17
|
+
let(:redis){ PulseMeter.redis }
|
18
|
+
let(:sample_event) {default_event || 123}
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
@interval_id = (Time.now.to_i / interval) * interval
|
22
|
+
@raw_data_key = sensor.raw_data_key(@interval_id)
|
23
|
+
@next_raw_data_key = sensor.raw_data_key(@interval_id + interval)
|
24
|
+
@start_of_interval = Time.at(@interval_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#dump" do
|
28
|
+
it "should be dumped succesfully" do
|
29
|
+
expect {sensor.dump!}.not_to raise_exception
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ".restore" do
|
34
|
+
before do
|
35
|
+
# no need to call sensor.dump! explicitly for it
|
36
|
+
# will be called automatically after creation
|
37
|
+
@restored = base_class.restore(sensor.name)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should restore #{described_class} instance" do
|
41
|
+
@restored.should be_instance_of(described_class)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should restore object with the same data" do
|
45
|
+
def inner_data(obj)
|
46
|
+
obj.instance_variables.sort.map {|v| obj.instance_variable_get(v)}
|
47
|
+
end
|
48
|
+
|
49
|
+
inner_data(sensor).should == inner_data(@restored)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#event" do
|
54
|
+
it "should write events to redis" do
|
55
|
+
expect{
|
56
|
+
sensor.event(sample_event)
|
57
|
+
}.to change{ redis.keys('*').count }.by(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should write data so that it totally expires after :raw_data_ttl" do
|
61
|
+
key_count = redis.keys('*').count
|
62
|
+
sensor.event(sample_event)
|
63
|
+
Timecop.freeze(Time.now + raw_data_ttl + 1) do
|
64
|
+
redis.keys('*').count.should == key_count
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should write data to bucket indicated by truncated timestamp" do
|
69
|
+
key = sensor.raw_data_key(@interval_id)
|
70
|
+
expect{
|
71
|
+
Timecop.freeze(@start_of_interval) do
|
72
|
+
sensor.event(sample_event)
|
73
|
+
end
|
74
|
+
}.to change{ redis.ttl(key) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#summarize" do
|
79
|
+
it "should convert data stored by raw_data_key to a value defined only by stored data" do
|
80
|
+
Timecop.freeze(@start_of_interval) do
|
81
|
+
sensor.event(sample_event)
|
82
|
+
end
|
83
|
+
Timecop.freeze(@start_of_interval + interval) do
|
84
|
+
sensor.event(sample_event)
|
85
|
+
end
|
86
|
+
sensor.summarize(@raw_data_key).should == sensor.summarize(@next_raw_data_key)
|
87
|
+
sensor.summarize(@raw_data_key).should_not be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#reduce" do
|
92
|
+
it "should store summarized value into data_key" do
|
93
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
94
|
+
val = sensor.summarize(@raw_data_key)
|
95
|
+
val.should_not be_nil
|
96
|
+
sensor.reduce(@interval_id)
|
97
|
+
redis.get(sensor.data_key(@interval_id)).should == val.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should remove original raw_data_key" do
|
101
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
102
|
+
expect{
|
103
|
+
sensor.reduce(@interval_id)
|
104
|
+
}.to change{ redis.keys(sensor.raw_data_key(@interval_id)).count }.from(1).to(0)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should expire stored summarized data" do
|
108
|
+
Timecop.freeze(@start_of_interval) do
|
109
|
+
sensor.event(sample_event)
|
110
|
+
sensor.reduce(@interval_id)
|
111
|
+
redis.keys(sensor.data_key(@interval_id)).count.should == 1
|
112
|
+
end
|
113
|
+
Timecop.freeze(@start_of_interval + ttl + 1) do
|
114
|
+
redis.keys(sensor.data_key(@interval_id)).count.should == 0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not store data if there is no corresponding raw data" do
|
119
|
+
Timecop.freeze(@start_of_interval) do
|
120
|
+
sensor.reduce(@interval_id)
|
121
|
+
redis.keys(sensor.data_key(@interval_id)).count.should == 0
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#reduce_all_raw" do
|
127
|
+
it "should reduce all data older than reduce_delay" do
|
128
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
129
|
+
val0 = sensor.summarize(@raw_data_key)
|
130
|
+
Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
|
131
|
+
val1 = sensor.summarize(@next_raw_data_key)
|
132
|
+
expect{
|
133
|
+
Timecop.freeze(@start_of_interval + interval + interval + reduce_delay + 1) { sensor.reduce_all_raw }
|
134
|
+
}.to change{ redis.keys(sensor.raw_data_key('*')).count }.from(2).to(0)
|
135
|
+
|
136
|
+
redis.get(sensor.data_key(@interval_id)).should == val0.to_s
|
137
|
+
redis.get(sensor.data_key(@interval_id + interval)).should == val1.to_s
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should not reduce fresh data" do
|
141
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
142
|
+
|
143
|
+
expect{
|
144
|
+
Timecop.freeze(@start_of_interval + interval + reduce_delay - 1) { sensor.reduce_all_raw }
|
145
|
+
}.not_to change{ redis.keys(sensor.raw_data_key('*')).count }
|
146
|
+
|
147
|
+
expect{
|
148
|
+
Timecop.freeze(@start_of_interval + interval + reduce_delay - 1) { sensor.reduce_all_raw }
|
149
|
+
}.not_to change{ redis.keys(sensor.data_key('*')).count }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe ".reduce_all_raw" do
|
154
|
+
it "should silently skip objects without reduce logic" do
|
155
|
+
dummy.dump!
|
156
|
+
expect {described_class.reduce_all_raw}.not_to raise_exception
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should send reduce_all_raw to all dumped objects" do
|
160
|
+
described_class.any_instance.should_receive(:reduce_all_raw)
|
161
|
+
described_class.reduce_all_raw
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#timeline_within" do
|
166
|
+
it "shoulde raise exception unless both arguments are Time objects" do
|
167
|
+
[:q, nil, -1].each do |bad_value|
|
168
|
+
expect{ sensor.timeline_within(Time.now, bad_value) }.to raise_exception(ArgumentError)
|
169
|
+
expect{ sensor.timeline_within(bad_value, Time.now) }.to raise_exception(ArgumentError)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should return an array of SensorData objects corresponding to stored data for passed interval" do
|
174
|
+
sensor.event(sample_event)
|
175
|
+
now = Time.now
|
176
|
+
timeline = sensor.timeline_within(now - 1, now)
|
177
|
+
timeline.should be_kind_of(Array)
|
178
|
+
timeline.each{|i| i.should be_kind_of(SensorData) }
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should return array of results containing as many results as there are sensor interval beginnings in the passed interval" do
|
182
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
183
|
+
Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
|
184
|
+
|
185
|
+
future = @start_of_interval + 3600
|
186
|
+
Timecop.freeze(future) do
|
187
|
+
sensor.timeline_within(
|
188
|
+
Time.at(@start_of_interval + interval - 1),
|
189
|
+
Time.at(@start_of_interval + interval + 1)
|
190
|
+
).size.should == 1
|
191
|
+
|
192
|
+
sensor.timeline_within(
|
193
|
+
Time.at(@start_of_interval - 1),
|
194
|
+
Time.at(@start_of_interval + interval + 1)
|
195
|
+
).size.should == 2
|
196
|
+
end
|
197
|
+
|
198
|
+
Timecop.freeze(@start_of_interval + interval + 2) do
|
199
|
+
sensor.timeline_within(
|
200
|
+
Time.at(@start_of_interval + interval + 1),
|
201
|
+
Time.at(@start_of_interval + interval + 2)
|
202
|
+
).size.should == 0
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "#timeline" do
|
208
|
+
|
209
|
+
it "should request timeline within interval from given number of seconds ago till now" do
|
210
|
+
Timecop.freeze do
|
211
|
+
now = Time.now
|
212
|
+
ago = interval * 100
|
213
|
+
sensor.timeline(ago).should == sensor.timeline_within(now - ago, now)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should return array of results containing as many results as there are sensor interval beginnings in the passed interval" do
|
218
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
219
|
+
Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
|
220
|
+
|
221
|
+
Timecop.freeze(@start_of_interval + interval + 1) do
|
222
|
+
sensor.timeline(2).size.should == 1
|
223
|
+
end
|
224
|
+
Timecop.freeze(@start_of_interval + interval + 2) do
|
225
|
+
sensor.timeline(1).size.should == 0
|
226
|
+
end
|
227
|
+
Timecop.freeze(@start_of_interval + interval + 1) do
|
228
|
+
sensor.timeline(2 + interval).size.should == 2
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "SensorData value for an interval" do
|
234
|
+
def check_sensor_data(sensor, value)
|
235
|
+
data = sensor.timeline(2).first
|
236
|
+
data.value.should == value
|
237
|
+
data.start_time.to_i.should == @interval_id
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should contain summarized value stored by data_key for reduced intervals" do
|
241
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
242
|
+
sensor.reduce(@interval_id)
|
243
|
+
Timecop.freeze(@start_of_interval + 1){
|
244
|
+
check_sensor_data(sensor, redis.get(sensor.data_key(@interval_id)))
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should contain summarized value based on raw data for intervals not yet reduced" do
|
249
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
250
|
+
Timecop.freeze(@start_of_interval + 1){
|
251
|
+
check_sensor_data(sensor, sensor.summarize(@raw_data_key))
|
252
|
+
}
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should contain nil for intervals without any data" do
|
256
|
+
Timecop.freeze(@start_of_interval + 1) {
|
257
|
+
check_sensor_data(sensor, nil)
|
258
|
+
}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe "#cleanup" do
|
263
|
+
it "should remove all sensor data (raw data, reduced data, annotations) from redis" do
|
264
|
+
Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
|
265
|
+
sensor.reduce(@interval_id)
|
266
|
+
Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
|
267
|
+
sensor.annotate("Fooo sensor")
|
268
|
+
|
269
|
+
sensor.cleanup
|
270
|
+
redis.keys('*').should be_empty
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|