active_metric 2.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +5 -0
  3. data/Rakefile +37 -0
  4. data/lib/active_metric.rb +38 -0
  5. data/lib/active_metric/axis.rb +11 -0
  6. data/lib/active_metric/behavior/calculates_derivative.rb +18 -0
  7. data/lib/active_metric/behavior/graph_calculation.rb +78 -0
  8. data/lib/active_metric/calculators/reservoir.rb +81 -0
  9. data/lib/active_metric/calculators/standard_deviator.rb +39 -0
  10. data/lib/active_metric/config/initializers/inflections.rb +4 -0
  11. data/lib/active_metric/graph_view_model.rb +60 -0
  12. data/lib/active_metric/measurement.rb +13 -0
  13. data/lib/active_metric/point_series_data.rb +17 -0
  14. data/lib/active_metric/report.rb +57 -0
  15. data/lib/active_metric/report_view_model.rb +106 -0
  16. data/lib/active_metric/sample.rb +148 -0
  17. data/lib/active_metric/series_data.rb +26 -0
  18. data/lib/active_metric/stat.rb +72 -0
  19. data/lib/active_metric/stat_definition.rb +20 -0
  20. data/lib/active_metric/statistics/defaults.rb +157 -0
  21. data/lib/active_metric/statistics/standard_deviation.rb +43 -0
  22. data/lib/active_metric/subject.rb +117 -0
  23. data/lib/active_metric/version.rb +3 -0
  24. data/test/active_metric_test.rb +30 -0
  25. data/test/axis_test.rb +22 -0
  26. data/test/behavior_tests/calculates_derivative_test.rb +35 -0
  27. data/test/behavior_tests/graph_calculation_test.rb +68 -0
  28. data/test/config/mongoid.yml +13 -0
  29. data/test/dummy/db/test.sqlite3 +0 -0
  30. data/test/dummy/log/test.log +18597 -0
  31. data/test/graph_view_model_test.rb +92 -0
  32. data/test/integration_test.rb +149 -0
  33. data/test/measurement_test.rb +45 -0
  34. data/test/mongoid_test.rb +24 -0
  35. data/test/point_series_data_test.rb +27 -0
  36. data/test/report_test.rb +73 -0
  37. data/test/report_view_model_test.rb +94 -0
  38. data/test/reservoir_test.rb +67 -0
  39. data/test/sample_test.rb +142 -0
  40. data/test/series_data_test.rb +20 -0
  41. data/test/standard_deviator_test.rb +45 -0
  42. data/test/stat_test.rb +222 -0
  43. data/test/subject_test.rb +22 -0
  44. data/test/test_helper.rb +76 -0
  45. metadata +123 -0
@@ -0,0 +1,67 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class ReservoirTest < ActiveSupport::TestCase
5
+
6
+ class Measurement
7
+ attr_accessor :value
8
+ end
9
+
10
+ setup do
11
+ @reservoir = Reservoir.new(10)
12
+ end
13
+
14
+ test "can initialize reservoir with type and size" do
15
+ assert_equal @reservoir.size, 10
16
+ end
17
+
18
+ test "can fill reservoir" do
19
+ @reservoir.fill create_measurement(1)
20
+ end
21
+
22
+ test "can fill reservoir and calculate percentile" do
23
+ (1..10).reverse_each do |value|
24
+ @reservoir.fill create_measurement(value)
25
+ end
26
+ assert_equal 9, @reservoir.calculate_percentile(0.8, :value)
27
+ assert_equal 10, @reservoir.calculate_percentile(0.98, :value)
28
+ end
29
+
30
+ test "calculates based on moving window" do
31
+ (1..20).reverse_each do |value|
32
+ @reservoir.fill create_measurement(value)
33
+ end
34
+
35
+ assert_equal 9, @reservoir.calculate_percentile(0.8, :value)
36
+ assert_equal 10, @reservoir.calculate_percentile(0.98, :value)
37
+ end
38
+
39
+ test "can calculate standard deviation for window" do
40
+ reverse_fill(1..20)
41
+ assert_close_to 2.87, @reservoir.calculate_standard_deviation(:value)
42
+ reverse_fill(95..100)
43
+ assert_close_to 46.56, @reservoir.calculate_standard_deviation(:value)
44
+ reverse_fill(90..95)
45
+ assert_close_to 2.47, @reservoir.calculate_standard_deviation(:value)
46
+ end
47
+
48
+ private
49
+
50
+ def reverse_fill(range)
51
+ range.reverse_each do |value|
52
+ @reservoir.fill create_measurement(value)
53
+ end
54
+ end
55
+
56
+ def shuffle(array)
57
+ array.size.downto(1) { |n| array.push array.delete_at(rand(n)) }
58
+ end
59
+
60
+ def create_measurement(value)
61
+ measurement = Measurement.new
62
+ measurement.value = value
63
+ measurement
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,142 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class SampleTest < ActiveSupport::TestCase
5
+
6
+ test "should have the correct stats by name" do
7
+ subject = TestSubject.new
8
+ sample = TestSample.new(:samplable => subject)
9
+ assert_equal 12, sample.stats.size
10
+ assert_kind_of Min, sample.min_value
11
+ assert_kind_of Mean, sample.mean_value
12
+ assert_kind_of Max, sample.max_value
13
+ assert_kind_of Eightieth, sample.eightieth_value
14
+ assert_kind_of NinetyEighth, sample.ninety_eighth_value
15
+ assert_kind_of StandardDeviation, sample.standard_deviation_value
16
+ assert_kind_of Delta, sample.delta_value
17
+ assert_kind_of Bucket, sample.bucket_value
18
+ assert_kind_of Speed, sample.speed_value
19
+ assert_kind_of PercentFalse, sample.percent_false_value
20
+ assert_kind_of TestCount, sample.test_count
21
+ assert_kind_of TestResponseCodes, sample.test_response_codes
22
+ end
23
+
24
+ test "samples spawned from samples contain seed measurement" do
25
+ subject = TestSubject.new
26
+
27
+ num_measurements = 5
28
+
29
+ sample = TestSample.new(:interval => 6, :samplable => subject)
30
+
31
+ num_measurements.times do |time|
32
+ sample.calculate TestMeasurement.new :value => 1, :timestamp => time
33
+ end
34
+
35
+ new_sample = sample.new_sample
36
+
37
+ assert_not_equal sample, new_sample
38
+ assert_equal 4, new_sample.seed_measurement.timestamp
39
+ assert_equal sample.latest_measurement, new_sample.seed_measurement
40
+ end
41
+
42
+
43
+ test "should have correct timestamp" do
44
+ measurements = [TestMeasurement.new(:value => 10, :timestamp => 1),
45
+ TestMeasurement.new(:value => 12, :timestamp => 2),
46
+ TestMeasurement.new(:value => 11, :timestamp => 9)]
47
+
48
+ subject = TestSubject.create
49
+ sample = TestSample.new(:samplable => subject)
50
+ measurements.each do |measurement|
51
+ sample.calculate(measurement)
52
+ end
53
+ assert_equal (4), sample.timestamp
54
+ end
55
+
56
+ test "should call calculate on all stats" do
57
+ subject = TestSubject.create
58
+ sample = TestSample.new(:samplable => subject)
59
+ 5.times do |value|
60
+ measurement = TestMeasurement.new :value => value, :timestamp => 1
61
+ sample.stats.each do |stat|
62
+ stat.expects(:calculate).with(measurement)
63
+ end
64
+ sample.calculate measurement
65
+ end
66
+
67
+ sample.stats.each do |stat|
68
+ stat.expects(:complete)
69
+ end
70
+ sample.complete
71
+ end
72
+
73
+ test "should return raw stat if sample does not have that stat" do
74
+ sample = TestSample.new
75
+ stat = sample.this_stat_does_not_exist
76
+ assert_equal 0, stat.value
77
+ end
78
+
79
+ test "should return stat if sample has it" do
80
+ sample = TestSample.new
81
+ stat = sample.min_value
82
+ assert stat.kind_of? Min
83
+ end
84
+
85
+ test "is summary" do
86
+ subject = TestSubject.create
87
+ summary = subject.summary
88
+ assert summary.is_summary?
89
+ end
90
+
91
+ test "summary only should set start time after first measurement" do
92
+ subject = TestSubject.create
93
+ summary = subject.summary
94
+ assert_nil summary.start_time
95
+
96
+
97
+ summary.calculate(TestMeasurement.new :value => 1, :timestamp => 1)
98
+ assert_equal 1, summary.start_time
99
+ end
100
+
101
+ test "duration from previous sample defaults to current sample duration if no previous sample" do
102
+ nil_seed_measurement = nil
103
+ sample = TestSample.new({},nil_seed_measurement)
104
+ sample.expects(:duration_in_seconds)
105
+ sample.duration_from_previous_sample_in_seconds
106
+ end
107
+
108
+ test "duration from previous sample returns correct duration" do
109
+ seed_measurement = mock
110
+
111
+ seed_measurement.expects(:timestamp).returns(100)
112
+
113
+ sample = TestSample.new({},{}, seed_measurement)
114
+ sample.expects(:end_time).returns(200)
115
+
116
+ assert_equal 100, sample.duration_from_previous_sample_in_seconds
117
+ end
118
+
119
+ test "passing in just a hash to create sets seed measurement to nil" do
120
+ subject = TestSubject.new
121
+
122
+ sample = TestSample.create( {:samplable => subject, :foo => "bar"} )
123
+ assert_nil sample.seed_measurement
124
+
125
+ end
126
+
127
+ test "stat definitions have proper axis" do
128
+ @stat_definitions = TestSample.stats_defined
129
+ assert_contains_axis(:min_value,0)
130
+ assert_contains_axis(:mean_value,0)
131
+ assert_contains_axis(:max_value,0)
132
+ assert_contains_axis(:standard_deviation_value,1)
133
+ assert_contains_axis(:test_count,1)
134
+ assert_contains_axis(:test_response_codes, -1)
135
+ end
136
+
137
+ def assert_contains_axis(stat,axis)
138
+ assert_equal axis, @stat_definitions.select {|sd| sd.access_name == stat}.first.options[:axis]
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,20 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class SeriesDataTest < ActiveSupport::TestCase
5
+
6
+ test "can instantiate from stat definition" do
7
+ access_name = "name_of_stat"
8
+ options = {axis: 2, visible: false}
9
+ stat_definition = StatDefinition.new(:property,Min,access_name,options)
10
+ series = SeriesData.from_stat_definition(stat_definition)
11
+
12
+ assert_equal access_name, series.label
13
+ assert_equal 2, series.y_axis
14
+ assert_equal 0, series.x_axis
15
+ assert_equal false, series.visible
16
+ end
17
+
18
+
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ require_relative "test_helper"
2
+ module ActiveMetric
3
+ class StandardDeviatorTest < ActiveSupport::TestCase
4
+
5
+ test "can calculate standard deviation" do
6
+ sd = StandardDeviator.new(:value)
7
+ test_stat(sd, 100.times)
8
+ assert_close_to 28.87, sd.standard_deviation
9
+ end
10
+
11
+ test "memoize standard deviation with a single measurement" do
12
+ sd = StandardDeviator.new(:value)
13
+ sd.expects(:calculate_standard_deviation).returns(5)
14
+ test_stat(sd, 1.times)
15
+ assert_equal 5, sd.standard_deviation
16
+ assert_equal 5, sd.standard_deviation
17
+ end
18
+
19
+ test "un-memoizes standard deviation with new measurement" do
20
+ sd = StandardDeviator.new(:value)
21
+ sd.expects(:calculate_standard_deviation).twice.returns(5,2)
22
+ test_stat(sd, 1.times)
23
+ assert_equal 5, sd.standard_deviation
24
+ assert_equal 5, sd.standard_deviation
25
+ test_stat(sd, 1.times)
26
+ assert_equal 2, sd.standard_deviation
27
+ end
28
+
29
+ test "can calculate standard deviation with no calculations " do
30
+ sd = StandardDeviator.new(:value)
31
+ assert_equal 0, sd.standard_deviation
32
+ end
33
+
34
+ private
35
+
36
+ def test_stat(stat, values)
37
+ values.each do |value|
38
+ stat.calculate TestMeasurement.new(:value => value)
39
+ end
40
+ end
41
+
42
+
43
+ end
44
+
45
+ end
data/test/stat_test.rb ADDED
@@ -0,0 +1,222 @@
1
+ require_relative 'test_helper'
2
+
3
+ module ActiveMetric
4
+ class StatTest < ActiveSupport::TestCase
5
+
6
+ setup do
7
+ @subject = TestSubject.create
8
+ @sample = TestSample.create({:samplable => @subject})
9
+ @stat = TestStat.new :property, :calculable => @sample
10
+ end
11
+
12
+ test "create a new stat" do
13
+ assert @stat
14
+ end
15
+
16
+ test "stat is a mongoid document" do
17
+ assert @stat.kind_of?(Mongoid::Document)
18
+ end
19
+
20
+ test "calculate raises an error if not implemented" do
21
+ assert_raise CannotInstantiateBaseStat do
22
+ @stat.calculate(mock)
23
+ end
24
+ end
25
+
26
+ test "has a dynamically defined name" do
27
+ assert_equal :test_stat_property, @stat.access_name
28
+ end
29
+
30
+ test "sets name for custom stat" do
31
+ proc = Proc.new { |m| self.value = m.value }
32
+ stat = Stat.create_custom_stat(:property, Integer, {}, proc).new(:property)
33
+ assert_equal :property, stat.access_name
34
+ end
35
+
36
+ test "custom stat uses block to calculate" do
37
+ proc = Proc.new { |m| self.value = m.value }
38
+ stat = Stat.create_custom_stat(:property, Integer, {}, proc).new(:property)
39
+ measurement = mock(:value => 10)
40
+ stat.calculate(measurement)
41
+ assert_equal 10, stat.value
42
+ end
43
+
44
+ test "can calculate min" do
45
+ stat = Min.new(:value, :calculable => @sample)
46
+ test_stat(stat, 10.times)
47
+ assert_equal 0, stat.value
48
+ end
49
+
50
+ test "can calculate max" do
51
+ stat = Max.new(:value, :calculable => @sample)
52
+ test_stat(stat, 10.times)
53
+ assert_equal 9, stat.value
54
+ end
55
+
56
+ test "can calculate mean" do
57
+ stat = Mean.new(:value, :calculable => @sample)
58
+ test_stat(stat, 10.times)
59
+ assert_equal 4.5, stat.value
60
+ end
61
+
62
+ test "can calculate derivative without seed_measurement" do
63
+ stat = Derivative.new(:value, :calculable => @sample)
64
+ @sample.stubs(:seed_measurement).returns(nil)
65
+
66
+ @sample.expects(:duration_from_previous_sample_in_seconds).times(2).returns(0, 1)
67
+ test_stat(stat, [1, 2])
68
+ slope_of_one = 1.0
69
+
70
+ assert_equal slope_of_one, stat.value
71
+ end
72
+
73
+ test "can calculate derivative with seed_measurement " do
74
+ stat = Derivative.new(:value, :calculable => @sample)
75
+
76
+ @sample.stubs(:seed_measurement).returns(test_seed_measurement)
77
+ @sample.expects(:duration_from_previous_sample_in_seconds).times(2).returns(1, 2)
78
+ test_stat(stat, [2, 3])
79
+
80
+ slope_of_one = 1.0
81
+
82
+ assert_equal slope_of_one, stat.value
83
+ end
84
+
85
+ test "can calculate last derivative without seed_measurement" do
86
+ stat = LastDerivative.new(:value, :calculable => @sample)
87
+ @sample.stubs(:seed_measurement).returns(nil)
88
+
89
+ test_stat(stat, [1])
90
+ no_derivative = 0
91
+
92
+ assert_equal no_derivative, stat.value
93
+ end
94
+
95
+ test "can calculate last derivative with seed measurement" do
96
+ stat = LastDerivative.new(:value, :calculable => @sample)
97
+
98
+ @sample.stubs(:seed_measurement).returns(test_seed_measurement)
99
+ test_stat(stat, [2, 4], [2, 3])
100
+
101
+ slope_of_two = 2.0
102
+
103
+ assert_equal slope_of_two, stat.value
104
+ end
105
+
106
+ test "can calculate speed" do
107
+ stat = Speed.new(:value, :calculable => @sample)
108
+
109
+ @sample.stubs(:seed_measurement).returns(test_seed_measurement)
110
+ @sample.expects(:duration_from_previous_sample_in_seconds).times(4).returns(1, 2, 3, 4)
111
+
112
+ test_stat(stat, [2, 3, 4, 5])
113
+
114
+ speed_of_one_per_second = 1.0
115
+ assert_equal speed_of_one_per_second, stat.value
116
+
117
+ end
118
+
119
+ test "can calculate delta without seed measurement" do
120
+ stat = Delta.new(:value, :calculable => @sample)
121
+ @sample.stubs(:seed_measurement).returns(nil)
122
+
123
+ test_stat(stat, [1, 5, 10])
124
+ assert_equal 9, stat.value
125
+ end
126
+
127
+ test "can calculate delta with seed measurement" do
128
+ stat = Delta.new(:value, :calculable => @sample)
129
+ @sample.stubs(:seed_measurement).returns(test_seed_measurement)
130
+
131
+ test_stat(stat, [5, 10])
132
+ assert_equal 9, stat.value
133
+ end
134
+
135
+ test "can calculate sum" do
136
+ stat = Sum.new(:value, :calculable => @sample)
137
+ test_stat(stat, 10.times)
138
+ assert_equal 45, stat.value
139
+ end
140
+
141
+ test "can calculate last" do
142
+ stat = Last.new(:value, :calculable => @sample)
143
+ test_stat(stat, 10.times)
144
+ assert_equal 9, stat.value
145
+ end
146
+
147
+ test "can calculate count" do
148
+ stat = Count.new(:value, :calculable => @sample)
149
+ test_stat(stat, 10.times)
150
+ assert_equal 10, stat.value
151
+ end
152
+
153
+ test "can calculate true count" do
154
+ stat = TrueCount.new(:value, :calculabe => @sample)
155
+ values = [true, true, false]
156
+ test_stat(stat, values)
157
+ assert_equal 2, stat.value
158
+ end
159
+
160
+ test "can calculate false count" do
161
+ stat = FalseCount.new(:value, :calculabe => @sample)
162
+ values = [true, true, false]
163
+ test_stat(stat, values)
164
+ assert_equal 1, stat.value
165
+ end
166
+
167
+ test "can bucket values" do
168
+ stat = Bucket.new(:value, :calculabe => @sample)
169
+ values = [200, 404, 500, 502, 200, 500, 504.2]
170
+ test_stat(stat, values)
171
+ expected_bucket = {
172
+ "200" => 2,
173
+ "404" => 1,
174
+ "500" => 2,
175
+ "502" => 1,
176
+ "504_2" => 1
177
+ }
178
+ assert_equal expected_bucket, stat.value
179
+ end
180
+
181
+ test "can calculate false percentage" do
182
+ stat = PercentFalse.new(:value, :calculabe => @sample)
183
+ values = [true, true, false, true]
184
+ test_stat(stat, values)
185
+ assert_equal 25, stat.value
186
+ end
187
+
188
+
189
+ #this test is here for the user, not for automated tests
190
+ #test "random distributions are good too" do
191
+ # stat = StandardDeviation.new(:value, :calculable => @sample)
192
+ # 100.times do
193
+ # stat.calculate TestMeasurement.create(:value => rand(100))
194
+ # end
195
+ # stat.complete
196
+ # assert_close_to 28.87, stat.value
197
+ #end
198
+
199
+ test "has subject" do
200
+ subject = TestSubject.create
201
+ sample = TestSample.create(:samplable => subject)
202
+ assert_equal subject, sample.stats.first.subject
203
+ end
204
+
205
+ private
206
+
207
+ def test_stat(stat, values, timestamps = values.to_a)
208
+ values.each_with_index do |value, index|
209
+ stat.calculate TestMeasurement.new(:value => value, :timestamp => timestamps[index])
210
+ end
211
+ stat.complete
212
+ end
213
+
214
+ def test_seed_measurement
215
+ seed_measurement = mock()
216
+ seed_measurement.stubs(:value).returns(1)
217
+ seed_measurement.stubs(:timestamp).returns(1)
218
+ seed_measurement
219
+ end
220
+
221
+ end
222
+ end