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,92 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class GraphViewModelTest < ActiveSupport::TestCase
5
+
6
+ EULERS_DAY= MONGO_MAX_LIMIT = (1 << 31) - 1
7
+
8
+ test "can retrieve series by label" do
9
+ gvm = GraphViewModel.create
10
+ gvm.series_data << generate_series_data(5, "first")
11
+ gvm.series_data << generate_series_data(5, "second")
12
+
13
+ series = gvm.series_for("second")
14
+
15
+ assert_equal "second", series.label
16
+
17
+ end
18
+
19
+ test "size returns 0 if no series_data" do
20
+ gvm = GraphViewModel.create
21
+ assert_equal 0, gvm.size
22
+ end
23
+
24
+ test "can create from definitions" do
25
+ axises_defintions = [{index: 1, label: "second"},
26
+ {index: 0, label: "first"},
27
+ {index: 0, label: "actually first"}]
28
+ options = {name: "test graph"}
29
+
30
+ stat_definitions = []
31
+ stat_definitions << stat_definition(true)
32
+ stat_definitions << stat_definition(true)
33
+ stat_definitions << stat_definition(false)
34
+
35
+ gvm = GraphViewModel.create_from_meta_data(axises_defintions, stat_definitions, options)
36
+
37
+ assert_equal 2, gvm.y_axises.size
38
+
39
+ index_zero_axises = gvm.y_axises.select{|axis| axis.index == 0}
40
+
41
+ assert_equal 1, index_zero_axises.size
42
+ assert_equal 0, index_zero_axises.first.index
43
+ assert_equal "actually first", index_zero_axises.first.label
44
+
45
+ assert_equal 2, gvm.series_data.size
46
+ end
47
+
48
+ test "can read y_axises sorted by index" do
49
+ axises_defintions = [{index: 1, label: "second"},
50
+ {index: 0, label: "first"},
51
+ {index: 0, label: "actually first"}]
52
+
53
+ gvm = GraphViewModel.create_from_meta_data(axises_defintions, [], {})
54
+
55
+ axises = gvm.ordered_y_axises
56
+
57
+ assert_equal 2, axises.size
58
+
59
+ assert_equal 0, axises.first.index
60
+ assert_equal "actually first", axises.first.label
61
+
62
+ assert_equal 1, axises.second.index
63
+ assert_equal "second", axises.second.label
64
+ end
65
+
66
+
67
+ test "can retrieve partial array" do
68
+ subject = Subject.create
69
+ graph_view_model = subject.graph_view_model
70
+ graph_view_model.series_data << generate_series_data(4)
71
+ graph_view_model.series_data << generate_series_data(4,"label 2")
72
+
73
+ partial_graph = subject.graph_view_model_starting_at(2)
74
+
75
+ assert_equal [[2,2],[3,3]], partial_graph.series_data.first.data
76
+ assert_equal [[2,2],[3,3]], partial_graph.series_data.second.data
77
+
78
+ end
79
+
80
+ def generate_series_data(x_count, label = "label")
81
+ data = []
82
+ x_count.times {|x| data << [x,x]}
83
+ PointSeriesData.new(data: data, label: label)
84
+ end
85
+
86
+ def stat_definition(graphable)
87
+ axis = graphable ? 0 : -1
88
+ StatDefinition.new(:name_of_stat,Min,"min_name_of_stat",{axis: axis})
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,149 @@
1
+ require_relative 'test_helper'
2
+
3
+ module ActiveMetric
4
+
5
+ class IntegrationTest < ActiveSupport::TestCase
6
+
7
+ test "can calculate correct eightieth percentile for subject" do
8
+ report = Report.create
9
+ subject = TestSubject.create :report => report
10
+ subject2 = TestSubject.create :report => report
11
+ 10.times do |value|
12
+ subject.calculate TestMeasurement.new(:value => 100 - value, :timestamp => value)
13
+ end
14
+ 10.times do |value|
15
+ subject2.calculate TestMeasurement.new(:value => 200 - value, :timestamp => value)
16
+ end
17
+ subject2.complete
18
+ subject.complete
19
+ assert_equal 99, subject.summary.eightieth_value.value
20
+ assert_equal 199, subject2.summary.eightieth_value.value
21
+ end
22
+
23
+ test "can calculate correct ninety eighth percentile for subject" do
24
+ report = Report.create
25
+ subject = TestSubject.create :report => report
26
+ subject2 = TestSubject.create :report => report
27
+ 5.times do |value|
28
+ subject.calculate TestMeasurement.new(:value => 100 - value, :timestamp => value)
29
+ end
30
+ 5.times do |value|
31
+ subject.calculate TestMeasurement.new(:value => 150 - value, :timestamp => value)
32
+ end
33
+ 10.times do |value|
34
+ subject2.calculate TestMeasurement.new(:value => 200 - value, :timestamp => value)
35
+ end
36
+ subject2.complete
37
+ subject.complete
38
+ assert_equal 150, subject.summary.ninety_eighth_value.value
39
+ assert_equal 200, subject2.summary.ninety_eighth_value.value
40
+ end
41
+
42
+ test "can calculate correct percentiles with multiple subjects" do
43
+ report = Report.create
44
+ subject = TestSubject.create :report => report
45
+ subject2 = TestSubject.create :report => report
46
+ 5.times do |value|
47
+ subject.calculate TestMeasurement.new(:value => 100 - value, :timestamp => value)
48
+ end
49
+ 5.times do |value|
50
+ subject.calculate TestMeasurement.new(:value => 150 - value, :timestamp => value)
51
+ end
52
+ 10.times do |value|
53
+ subject2.calculate TestMeasurement.new(:value => 155 - value, :timestamp => value)
54
+ end
55
+ subject2.complete
56
+ subject.complete
57
+
58
+ assert_equal 149, subject.summary.eightieth_value.value
59
+ assert_equal 150, subject.summary.ninety_eighth_value.value
60
+
61
+ assert_equal 154, subject2.summary.eightieth_value.value
62
+ assert_equal 155, subject2.summary.ninety_eighth_value.value
63
+ end
64
+
65
+ test "can calculate standard deviations" do
66
+ report = Report.create
67
+ subject = TestSubject.create :report => report
68
+ 10.times do |value|
69
+ subject.calculate TestMeasurement.new(:value => 100 - value, :timestamp => value)
70
+ end
71
+
72
+ subject.complete
73
+ assert_close_to 2.87, subject.summary.standard_deviation_value.value
74
+ end
75
+
76
+ test "summary should update when samples changes" do
77
+ report = Report.create
78
+ subject = TestSubject.create :report => report
79
+ 6.times do |value|
80
+ subject.calculate TestMeasurement.new(:value => value, :timestamp => value)
81
+ end
82
+
83
+ assert_equal 4.0, subject.summary.eightieth_value.value
84
+
85
+ subject.calculate TestMeasurement.new(:value => 6, :timestamp => 6)
86
+
87
+ assert_equal 4.0, subject.summary.eightieth_value.value
88
+ end
89
+
90
+ test "subjects should delegate to summaries" do
91
+ report = Report.create
92
+ subject = TestSubject.create :report => report
93
+ 6.times do |value|
94
+ subject.calculate TestMeasurement.new(:value => value, :timestamp => value)
95
+ end
96
+
97
+ assert_equal 4.0, subject.eightieth_value
98
+ assert_equal 4.0, subject.eightieth_value
99
+ end
100
+
101
+ test "subjects should delegate to document before summaries" do
102
+ report = Report.create
103
+ subject = TestSubject.create :report => report, :test_field => "subject name"
104
+ 6.times do |value|
105
+ subject.calculate TestMeasurement.new(:value => value, :timestamp => value)
106
+ end
107
+ assert_equal "subject name", subject.test_field
108
+ assert_equal 4.0, subject.eightieth_value
109
+ end
110
+
111
+ test "subjects should be able to delegate to non stat properties on summary" do
112
+ report = Report.create
113
+ subject = TestSubject.create :report => report
114
+ 6.times do |value|
115
+ subject.calculate TestMeasurement.new(:value => value, :timestamp => value)
116
+ end
117
+ assert_equal 5, subject.duration_in_seconds
118
+ assert_equal 5, subject.duration_in_seconds
119
+ end
120
+
121
+ test "no measurements or interval samples should be in the database" do
122
+ report = Report.create
123
+ subject = TestSubject.create :report => report
124
+ subject2 = TestSubject.create :report => report
125
+ 11.times do |value|
126
+ subject.calculate TestMeasurement.new(:value => 100 - value, :timestamp => value)
127
+ end
128
+ 10.times do |value|
129
+ subject2.calculate TestMeasurement.new(:value => 200 - value, :timestamp => value)
130
+ end
131
+ subject2.complete
132
+ subject.complete
133
+ assert_equal 90, subject.min_value
134
+ assert_equal 191, subject2.min_value
135
+
136
+ assert_equal 0, ActiveMetric::Measurement.count
137
+ assert_equal 0, ActiveMetric::Sample.where(:interval.ne => nil).count
138
+ assert_equal 2, ActiveMetric::Sample.where(:interval => nil).count
139
+ end
140
+
141
+ private
142
+
143
+ def assert_within_threshold(threshold, actual, estimated)
144
+ range_val = actual * threshold
145
+ range = ((actual - range_val)..(actual + range_val))
146
+ assert_within_range range, estimated
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'test_helper'
2
+
3
+ module ActiveMetric
4
+
5
+ class MeasurementTest < ActiveSupport::TestCase
6
+
7
+ setup do
8
+ @measurement = TestMeasurement.new
9
+ end
10
+
11
+ test 'can create measurement' do
12
+ assert @measurement
13
+ end
14
+
15
+ test 'is a mongoid document' do
16
+ assert @measurement.kind_of? Mongoid::Document
17
+ end
18
+
19
+ test 'time returns datetime in seconds for timestamp' do
20
+ @measurement.timestamp = 1318338826385
21
+ assert_equal Time.at(1318338826), @measurement.time
22
+ end
23
+
24
+ test "converts appropriate fields to integer" do
25
+ assert_saves_as_integer @measurement, :timestamp
26
+ end
27
+
28
+ test "measurements default timestamp to now" do
29
+ assert @measurement.timestamp
30
+ sleep(1)
31
+ measurement2 = TestMeasurement.new
32
+ assert measurement2.timestamp
33
+
34
+ assert_not_equal @measurement.timestamp, measurement2.timestamp
35
+ end
36
+
37
+ private
38
+
39
+ def assert_saves_as_integer(measurement, field)
40
+ measurement.send("#{field}=","12345")
41
+ measurement.save!
42
+ assert_equal 12345, measurement.send(field)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MongoidTest < ActiveSupport::TestCase
4
+
5
+
6
+ test "inherited documents from class dot new dont get a type" do
7
+ class TopLevel
8
+ include Mongoid::Document
9
+ end
10
+
11
+ class SecondLevel < TopLevel
12
+ end
13
+
14
+ second_level = SecondLevel.create
15
+ assert_equal "MongoidTest::SecondLevel", second_level._type
16
+
17
+ klass = Class.new(TopLevel)
18
+ MongoidTest.const_set(:NewKlass, klass)
19
+
20
+ new_object = NewKlass.create
21
+ assert_equal "MongoidTest::NewKlass", new_object._type
22
+ #assert_equal nil, new_object._type, "Mongoid correctly sets _type on Anonymous Classes now. Go remove the hack in stat: create_custom_stat WRT _type "
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class PointSeriesDataTest < ActiveSupport::TestCase
5
+
6
+ test "can push and pop atomically" do
7
+ gvm = GraphViewModel.create
8
+ psd = PointSeriesData.new(data: [[1,1],[2,2]])
9
+ gvm.series_data << psd
10
+
11
+ psd.pop_data
12
+
13
+ assert_equal [[1,1]], psd.data
14
+
15
+ psd.push_data([3,3])
16
+
17
+ assert_equal [[1,1],[3,3]], psd.data
18
+ end
19
+
20
+ test "can calculate size of data" do
21
+ psd = PointSeriesData.new(data: [[1,1],[2,2]])
22
+
23
+ assert_equal 2, psd.size
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,73 @@
1
+ require_relative 'test_helper'
2
+
3
+ module ActiveMetric
4
+
5
+ class ReportTest < ActiveSupport::TestCase
6
+
7
+
8
+ test "method missing for subjects" do
9
+ report = Report.create
10
+ subjects = 2.times.map {TestSubject.create :report => report}
11
+ assert_equal subjects, report.test_subjects
12
+ end
13
+
14
+ test "responds properly to method missing if not a subject name" do
15
+ report = Report.create
16
+ assert_raises NoMethodError do
17
+ report.bad_method
18
+ end
19
+ end
20
+
21
+ test "can still use dynamic fields with overwritten method missing" do
22
+ report = Report.create :some_random_field => "value"
23
+ assert_equal "value", report.some_random_field
24
+ end
25
+
26
+ test "can still access dynamic fields with subjects in name" do
27
+ report = Report.create :some_subjects => "value"
28
+ assert_equal "value", report.some_subjects
29
+ end
30
+
31
+ test "can delete a report" do
32
+ report = Report.create
33
+ report.subjects.create
34
+
35
+ report.delete
36
+
37
+ assert_equal 0, ActiveMetric::Report.count
38
+ assert_equal 0, ActiveMetric::Subject.count
39
+ assert_equal 0, ActiveMetric::Sample.count
40
+ assert_equal 0, ActiveMetric::GraphViewModel.count
41
+
42
+ end
43
+
44
+ test "can set display name" do
45
+ class TestReport < Report
46
+
47
+ def set_display_name
48
+ "Overriden Display Name"
49
+ end
50
+ end
51
+
52
+ report = TestReport.create
53
+
54
+ assert_equal "Overriden Display Name", report.display_name
55
+ end
56
+
57
+ test "can set display name through attribute" do
58
+ class TestReport < Report
59
+
60
+ def set_display_name
61
+ "TestReport Display Name"
62
+ end
63
+ end
64
+
65
+ report = TestReport.create display_name: "Overriden Display Name"
66
+
67
+ assert_equal "Overriden Display Name", report.display_name
68
+ end
69
+
70
+
71
+
72
+ end
73
+ end
@@ -0,0 +1,94 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveMetric
4
+ class ReportViewModelTest < ActiveSupport::TestCase
5
+
6
+ class TestReportViewModel < ActiveMetric::ReportViewModel
7
+
8
+ table :table1 do
9
+ column "header 1", :field1
10
+ column "header 2", :field2, :precision => 2
11
+ column "header 3", :field3, :format => :duration_with_format
12
+ end
13
+
14
+ table :table2 do
15
+ column "header 1", :field1
16
+ column "header 2", :field2
17
+ end
18
+
19
+ end
20
+
21
+ test "can instantiate a view model" do
22
+ rvm = TestReportViewModel.new
23
+ rvm.add_table :table1, subjects
24
+ table = rvm.tables.first
25
+
26
+ expected_headers = ["header 1", "header 2", "header 3"]
27
+ assert_equal expected_headers, table.headers
28
+
29
+ expected_cells = ["name",2.8345, 3]
30
+ expected_cell_options = [{},{:precision =>2},{:format => :duration_with_format}]
31
+ table.rows.each do |row|
32
+ assert_equal expected_cells, row.cells.map(&:value)
33
+ assert_equal expected_cell_options, row.cells.map(&:format_options)
34
+ assert row.has_series
35
+ end
36
+ end
37
+
38
+ test "cannot add a table that does not exist" do
39
+ rvm = TestReportViewModel.new
40
+ assert_raise ReportViewModel::TableDoesNotExist do
41
+ rvm.add_table :bad_table, subjects
42
+ end
43
+ end
44
+
45
+ test "can have multiple tables" do
46
+ rvm = TestReportViewModel.new
47
+ rvm.add_table :table1, subjects
48
+ rvm.add_table :table2, subjects
49
+ rvm.add_table :table1, subjects
50
+
51
+ table1_headers = ["header 1", "header 2", "header 3"]
52
+ table2_headers = ["header 1", "header 2"]
53
+
54
+ assert_equal table1_headers, rvm.tables[0].headers
55
+ assert_equal table2_headers, rvm.tables[1].headers
56
+ assert_equal table1_headers, rvm.tables[2].headers
57
+
58
+ table1_data = ["name",2.8345, 3]
59
+ table2_data = ["name",2.8345]
60
+ table3_data = ["name",2.8345, 3]
61
+
62
+ assert_table_data table1_data, rvm.tables[0]
63
+ assert_table_data table2_data, rvm.tables[1]
64
+ assert_table_data table3_data, rvm.tables[2]
65
+ end
66
+
67
+
68
+ def assert_table_data(data, table)
69
+ table.rows.each do |row|
70
+ assert_equal data, row.cells.map(&:value)
71
+ assert_equal "1", row.row_id
72
+ end
73
+ end
74
+
75
+ def subjects
76
+ subject = create_row_data(field1: "name",
77
+ field2: 2.8345,
78
+ field3: 3)
79
+
80
+ subject.stubs(:to_param).returns("1")
81
+ subject.stubs(:has_graph_data).returns(true)
82
+ [subject]
83
+ end
84
+
85
+ def create_row_data(values)
86
+ row_data = mock
87
+ values.each do |key,value|
88
+ row_data.stubs(key).returns(value)
89
+ end
90
+ row_data
91
+ end
92
+
93
+ end
94
+ end