airbrake-ruby 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake-ruby.rb +515 -0
  3. data/lib/airbrake-ruby/async_sender.rb +80 -0
  4. data/lib/airbrake-ruby/backtrace.rb +196 -0
  5. data/lib/airbrake-ruby/benchmark.rb +39 -0
  6. data/lib/airbrake-ruby/code_hunk.rb +51 -0
  7. data/lib/airbrake-ruby/config.rb +229 -0
  8. data/lib/airbrake-ruby/config/validator.rb +91 -0
  9. data/lib/airbrake-ruby/deploy_notifier.rb +36 -0
  10. data/lib/airbrake-ruby/file_cache.rb +54 -0
  11. data/lib/airbrake-ruby/filter_chain.rb +95 -0
  12. data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
  13. data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
  14. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +46 -0
  15. data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
  16. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +92 -0
  17. data/lib/airbrake-ruby/filters/git_repository_filter.rb +64 -0
  18. data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
  19. data/lib/airbrake-ruby/filters/keys_blacklist.rb +49 -0
  20. data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
  21. data/lib/airbrake-ruby/filters/keys_whitelist.rb +48 -0
  22. data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
  23. data/lib/airbrake-ruby/filters/sql_filter.rb +125 -0
  24. data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
  25. data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
  26. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  27. data/lib/airbrake-ruby/ignorable.rb +44 -0
  28. data/lib/airbrake-ruby/inspectable.rb +39 -0
  29. data/lib/airbrake-ruby/loggable.rb +34 -0
  30. data/lib/airbrake-ruby/monotonic_time.rb +43 -0
  31. data/lib/airbrake-ruby/nested_exception.rb +38 -0
  32. data/lib/airbrake-ruby/notice.rb +162 -0
  33. data/lib/airbrake-ruby/notice_notifier.rb +134 -0
  34. data/lib/airbrake-ruby/performance_breakdown.rb +46 -0
  35. data/lib/airbrake-ruby/performance_notifier.rb +155 -0
  36. data/lib/airbrake-ruby/promise.rb +109 -0
  37. data/lib/airbrake-ruby/query.rb +54 -0
  38. data/lib/airbrake-ruby/request.rb +46 -0
  39. data/lib/airbrake-ruby/response.rb +74 -0
  40. data/lib/airbrake-ruby/stashable.rb +15 -0
  41. data/lib/airbrake-ruby/stat.rb +73 -0
  42. data/lib/airbrake-ruby/sync_sender.rb +113 -0
  43. data/lib/airbrake-ruby/tdigest.rb +393 -0
  44. data/lib/airbrake-ruby/thread_pool.rb +128 -0
  45. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  46. data/lib/airbrake-ruby/timed_trace.rb +58 -0
  47. data/lib/airbrake-ruby/truncator.rb +115 -0
  48. data/lib/airbrake-ruby/version.rb +6 -0
  49. data/spec/airbrake_spec.rb +324 -0
  50. data/spec/async_sender_spec.rb +72 -0
  51. data/spec/backtrace_spec.rb +427 -0
  52. data/spec/benchmark_spec.rb +33 -0
  53. data/spec/code_hunk_spec.rb +115 -0
  54. data/spec/config/validator_spec.rb +184 -0
  55. data/spec/config_spec.rb +154 -0
  56. data/spec/deploy_notifier_spec.rb +48 -0
  57. data/spec/file_cache_spec.rb +34 -0
  58. data/spec/filter_chain_spec.rb +92 -0
  59. data/spec/filters/context_filter_spec.rb +23 -0
  60. data/spec/filters/dependency_filter_spec.rb +12 -0
  61. data/spec/filters/exception_attributes_filter_spec.rb +50 -0
  62. data/spec/filters/gem_root_filter_spec.rb +41 -0
  63. data/spec/filters/git_last_checkout_filter_spec.rb +46 -0
  64. data/spec/filters/git_repository_filter.rb +61 -0
  65. data/spec/filters/git_revision_filter_spec.rb +126 -0
  66. data/spec/filters/keys_blacklist_spec.rb +225 -0
  67. data/spec/filters/keys_whitelist_spec.rb +194 -0
  68. data/spec/filters/root_directory_filter_spec.rb +39 -0
  69. data/spec/filters/sql_filter_spec.rb +262 -0
  70. data/spec/filters/system_exit_filter_spec.rb +14 -0
  71. data/spec/filters/thread_filter_spec.rb +277 -0
  72. data/spec/fixtures/notroot.txt +7 -0
  73. data/spec/fixtures/project_root/code.rb +221 -0
  74. data/spec/fixtures/project_root/empty_file.rb +0 -0
  75. data/spec/fixtures/project_root/long_line.txt +1 -0
  76. data/spec/fixtures/project_root/short_file.rb +3 -0
  77. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
  78. data/spec/helpers.rb +9 -0
  79. data/spec/ignorable_spec.rb +14 -0
  80. data/spec/inspectable_spec.rb +45 -0
  81. data/spec/monotonic_time_spec.rb +12 -0
  82. data/spec/nested_exception_spec.rb +73 -0
  83. data/spec/notice_notifier/options_spec.rb +259 -0
  84. data/spec/notice_notifier_spec.rb +356 -0
  85. data/spec/notice_spec.rb +296 -0
  86. data/spec/performance_breakdown_spec.rb +12 -0
  87. data/spec/performance_notifier_spec.rb +491 -0
  88. data/spec/promise_spec.rb +197 -0
  89. data/spec/query_spec.rb +11 -0
  90. data/spec/request_spec.rb +11 -0
  91. data/spec/response_spec.rb +88 -0
  92. data/spec/spec_helper.rb +100 -0
  93. data/spec/stashable_spec.rb +23 -0
  94. data/spec/stat_spec.rb +47 -0
  95. data/spec/sync_sender_spec.rb +133 -0
  96. data/spec/tdigest_spec.rb +230 -0
  97. data/spec/thread_pool_spec.rb +158 -0
  98. data/spec/time_truncate_spec.rb +13 -0
  99. data/spec/timed_trace_spec.rb +125 -0
  100. data/spec/truncator_spec.rb +238 -0
  101. metadata +216 -0
@@ -0,0 +1,197 @@
1
+ RSpec.describe Airbrake::Promise do
2
+ describe ".then" do
3
+ let(:resolved_with) { [] }
4
+ let(:rejected_with) { [] }
5
+
6
+ context "when it is not resolved" do
7
+ it "returns self" do
8
+ expect(subject.then {}).to eq(subject)
9
+ end
10
+
11
+ it "doesn't call the resolve callbacks yet" do
12
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
13
+ expect(resolved_with).to be_empty
14
+ end
15
+ end
16
+
17
+ context "when it is resolved" do
18
+ shared_examples "then specs" do
19
+ it "returns self" do
20
+ expect(subject.then {}).to eq(subject)
21
+ end
22
+
23
+ it "yields the resolved value" do
24
+ yielded = nil
25
+ subject.then { |value| yielded = value }
26
+ expect(yielded).to eq('id' => '123')
27
+ end
28
+
29
+ it "calls the resolve callbacks" do
30
+ expect(resolved_with).to match_array([1, 2])
31
+ end
32
+
33
+ it "doesn't call the reject callbacks" do
34
+ expect(rejected_with).to be_empty
35
+ end
36
+ end
37
+
38
+ context "and there are some resolve and reject callbacks in place" do
39
+ before do
40
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
41
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
42
+ subject.resolve('id' => '123')
43
+ end
44
+
45
+ include_examples "then specs"
46
+
47
+ it "registers the resolve callbacks" do
48
+ subject.resolve('id' => '456')
49
+ expect(resolved_with).to match_array([1, 2, 1, 2])
50
+ end
51
+ end
52
+
53
+ context "and additional then callbacks are added" do
54
+ before do
55
+ subject.resolve('id' => '123')
56
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
57
+ end
58
+
59
+ include_examples "then specs"
60
+
61
+ it "doesn't register new resolve callbacks" do
62
+ subject.resolve('id' => '456')
63
+ expect(resolved_with).to match_array([1, 2])
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ".rescue" do
70
+ let(:resolved_with) { [] }
71
+ let(:rejected_with) { [] }
72
+
73
+ context "when it is not rejected" do
74
+ it "returns self" do
75
+ expect(subject.then {}).to eq(subject)
76
+ end
77
+
78
+ it "doesn't call the reject callbacks yet" do
79
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
80
+ expect(rejected_with).to be_empty
81
+ end
82
+ end
83
+
84
+ context "when it is rejected" do
85
+ shared_examples "rescue specs" do
86
+ it "returns self" do
87
+ expect(subject.rescue {}).to eq(subject)
88
+ end
89
+
90
+ it "yields the rejected value" do
91
+ yielded = nil
92
+ subject.rescue { |value| yielded = value }
93
+ expect(yielded).to eq('bingo')
94
+ end
95
+
96
+ it "doesn't call the resolve callbacks" do
97
+ expect(resolved_with).to be_empty
98
+ end
99
+
100
+ it "calls the reject callbacks" do
101
+ expect(rejected_with).to match_array([1, 2])
102
+ end
103
+ end
104
+
105
+ context "and there are some resolve and reject callbacks in place" do
106
+ before do
107
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
108
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
109
+ subject.reject('bingo')
110
+ end
111
+
112
+ include_examples "rescue specs"
113
+
114
+ it "registers the reject callbacks" do
115
+ subject.reject('bingo again')
116
+ expect(rejected_with).to match_array([1, 2, 1, 2])
117
+ end
118
+ end
119
+
120
+ context "and additional reject callbacks are added" do
121
+ before do
122
+ subject.reject('bingo')
123
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
+ end
125
+
126
+ include_examples "rescue specs"
127
+
128
+ it "doesn't register new reject callbacks" do
129
+ subject.reject('bingo again')
130
+ expect(rejected_with).to match_array([1, 2])
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe ".resolve" do
137
+ it "returns self" do
138
+ expect(subject.resolve(1)).to eq(subject)
139
+ end
140
+
141
+ it "executes callbacks attached with .then" do
142
+ array = []
143
+ subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
144
+
145
+ expect(array.size).to be_zero
146
+ subject.resolve(1)
147
+ expect(array).to match_array([1])
148
+ end
149
+ end
150
+
151
+ describe ".reject" do
152
+ it "returns self" do
153
+ expect(subject.reject(1)).to eq(subject)
154
+ end
155
+
156
+ it "executes callbacks attached with .rescue" do
157
+ array = []
158
+ subject.then { array << 1 }.rescue { |error| array << error }
159
+
160
+ expect(array.size).to be_zero
161
+ subject.reject(999)
162
+ expect(array).to match_array([999])
163
+ end
164
+ end
165
+
166
+ describe "#rejected?" do
167
+ context "when it was rejected" do
168
+ before { subject.reject(1) }
169
+ it { is_expected.to be_rejected }
170
+ end
171
+
172
+ context "when it wasn't rejected" do
173
+ it { is_expected.not_to be_rejected }
174
+ end
175
+
176
+ context "when it was resolved" do
177
+ before { subject.resolve }
178
+ it { is_expected.not_to be_rejected }
179
+ end
180
+ end
181
+
182
+ describe "#resolved?" do
183
+ context "when it was resolved" do
184
+ before { subject.resolve }
185
+ it { is_expected.to be_resolved }
186
+ end
187
+
188
+ context "when it wasn't resolved" do
189
+ it { is_expected.not_to be_resolved }
190
+ end
191
+
192
+ context "when it was rejected" do
193
+ before { subject.reject(1) }
194
+ it { is_expected.not_to be_resolved }
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,11 @@
1
+ RSpec.describe Airbrake::Query do
2
+ describe "#stash" do
3
+ subject do
4
+ described_class.new(
5
+ method: 'GET', route: '/', query: '', start_time: Time.now
6
+ )
7
+ end
8
+
9
+ it { is_expected.to respond_to(:stash) }
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ RSpec.describe Airbrake::Request do
2
+ describe "#stash" do
3
+ subject do
4
+ described_class.new(
5
+ method: 'GET', route: '/', status_code: 200, start_time: Time.now
6
+ )
7
+ end
8
+
9
+ it { is_expected.to respond_to(:stash) }
10
+ end
11
+ end
@@ -0,0 +1,88 @@
1
+ RSpec.describe Airbrake::Response do
2
+ describe ".parse" do
3
+ [200, 201, 204].each do |code|
4
+ context "when response code is #{code}" do
5
+ it "logs response body" do
6
+ expect(Airbrake::Loggable.instance).to receive(:debug).with(
7
+ /Airbrake::Response \(#{code}\): {}/
8
+ )
9
+ described_class.parse(OpenStruct.new(code: code, body: '{}'))
10
+ end
11
+ end
12
+ end
13
+
14
+ [400, 401, 403, 420].each do |code|
15
+ context "when response code is #{code}" do
16
+ it "logs response message" do
17
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
18
+ /Airbrake: foo/
19
+ )
20
+ described_class.parse(
21
+ OpenStruct.new(code: code, body: '{"message":"foo"}')
22
+ )
23
+ end
24
+ end
25
+ end
26
+
27
+ context "when response code is 429" do
28
+ let(:response) { OpenStruct.new(code: 429, body: '{"message":"rate limited"}') }
29
+
30
+ it "logs response message" do
31
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
32
+ /Airbrake: rate limited/
33
+ )
34
+ described_class.parse(response)
35
+ end
36
+
37
+ it "returns an error response" do
38
+ time = Time.now
39
+ allow(Time).to receive(:now).and_return(time)
40
+
41
+ resp = described_class.parse(response)
42
+ expect(resp).to include(
43
+ 'error' => '**Airbrake: rate limited',
44
+ 'rate_limit_reset' => time
45
+ )
46
+ end
47
+ end
48
+
49
+ context "when response code is unhandled" do
50
+ let(:response) { OpenStruct.new(code: 500, body: 'foo') }
51
+
52
+ it "logs response body" do
53
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
54
+ /Airbrake: unexpected code \(500\)\. Body: foo/
55
+ )
56
+ described_class.parse(response)
57
+ end
58
+
59
+ it "returns an error response" do
60
+ resp = described_class.parse(response)
61
+ expect(resp).to eq('error' => 'foo')
62
+ end
63
+
64
+ it "truncates body" do
65
+ response.body *= 1000
66
+ resp = described_class.parse(response)
67
+ expect(resp).to eq('error' => ('foo' * 33) + 'fo...')
68
+ end
69
+ end
70
+
71
+ context "when response body can't be parsed as JSON" do
72
+ let(:response) { OpenStruct.new(code: 201, body: 'foo') }
73
+
74
+ it "logs response body" do
75
+ expect(Airbrake::Loggable.instance).to receive(:error).with(
76
+ /Airbrake: error while parsing body \(.*unexpected token.*\)\. Body: foo/
77
+ )
78
+ described_class.parse(response)
79
+ end
80
+
81
+ it "returns an error message" do
82
+ expect(described_class.parse(response)['error']).to match(
83
+ /\A#<JSON::ParserError.+>/
84
+ )
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,100 @@
1
+ require 'simplecov'
2
+ SimpleCov.start if ENV['COVERAGE']
3
+
4
+ require 'airbrake-ruby'
5
+
6
+ require 'rspec/its'
7
+
8
+ require 'webmock'
9
+ require 'webmock/rspec'
10
+ require 'pry'
11
+
12
+ require 'pathname'
13
+ require 'webrick'
14
+ require 'English'
15
+ require 'base64'
16
+ require 'pp'
17
+
18
+ require 'helpers'
19
+
20
+ RSpec.configure do |c|
21
+ c.order = 'random'
22
+ c.color = true
23
+ c.disable_monkey_patching!
24
+ c.include Helpers
25
+ end
26
+
27
+ Thread.abort_on_exception = true
28
+
29
+ WebMock.disable_net_connect!(allow_localhost: true)
30
+
31
+ class AirbrakeTestError < RuntimeError
32
+ attr_reader :backtrace
33
+
34
+ def initialize(*)
35
+ super
36
+ # rubocop:disable Metrics/LineLength
37
+ @backtrace = [
38
+ "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
39
+ "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
40
+ "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
41
+ "/home/kyrylo/code/airbrake/ruby/spec/airbrake_spec.rb:1:in `<top (required)>'",
42
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `load'",
43
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1327:in `block in load_spec_files'",
44
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `each'",
45
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb:1325:in `load_spec_files'",
46
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:102:in `setup'",
47
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:88:in `run'",
48
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:73:in `run'",
49
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:41:in `invoke'",
50
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
51
+ ]
52
+ # rubocop:enable Metrics/LineLength
53
+ end
54
+
55
+ # rubocop:disable Naming/AccessorMethodName
56
+ def set_backtrace(backtrace)
57
+ @backtrace = backtrace
58
+ end
59
+ # rubocop:enable Naming/AccessorMethodName
60
+
61
+ def message
62
+ 'App crashed!'
63
+ end
64
+ end
65
+
66
+ class JavaAirbrakeTestError < AirbrakeTestError
67
+ def initialize(*)
68
+ super
69
+ # rubocop:disable Metrics/LineLength
70
+ @backtrace = [
71
+ "org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:26)",
72
+ "org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL(Interpreter.java:126)",
73
+ "org.jruby.RubyKernel$INVOKER$s$0$3$eval19.call(RubyKernel$INVOKER$s$0$3$eval19.gen)",
74
+ "org.jruby.RubyKernel$INVOKER$s$0$0$loop.call(RubyKernel$INVOKER$s$0$0$loop.gen)",
75
+ "org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:139)",
76
+ "org.jruby.RubyKernel$INVOKER$s$rbCatch19.call(RubyKernel$INVOKER$s$rbCatch19.gen)",
77
+ "opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.invokeOther4:start(/opt/rubies/jruby-9.0.0.0/bin/irb)",
78
+ "opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.RUBY$script(/opt/rubies/jruby-9.0.0.0/bin/irb:13)",
79
+ "org.jruby.ir.Compiler$1.load(Compiler.java:111)",
80
+ "org.jruby.Main.run(Main.java:225)",
81
+ "org.jruby.Main.main(Main.java:197)"
82
+ ]
83
+ # rubocop:enable Metrics/LineLength
84
+ end
85
+
86
+ def is_a?(*)
87
+ true
88
+ end
89
+ end
90
+
91
+ class Ruby21Error < RuntimeError
92
+ attr_accessor :cause
93
+
94
+ def self.raise_error(msg)
95
+ ex = new(msg)
96
+ ex.cause = $ERROR_INFO
97
+
98
+ raise ex
99
+ end
100
+ end
@@ -0,0 +1,23 @@
1
+ RSpec.describe Airbrake::Stashable do
2
+ let(:klass) do
3
+ mod = described_class
4
+ Class.new { include(mod) }
5
+ end
6
+
7
+ describe "#stash" do
8
+ subject { klass.new }
9
+
10
+ it "returns a hash" do
11
+ expect(subject.stash).to be_a(Hash)
12
+ end
13
+
14
+ it "returns an empty hash" do
15
+ expect(subject.stash).to be_empty
16
+ end
17
+
18
+ it "remembers what was put in the stash" do
19
+ subject.stash[:foo] = 1
20
+ expect(subject.stash[:foo]).to eq(1)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ RSpec.describe Airbrake::Stat do
2
+ describe "#to_h" do
3
+ it "converts to a hash" do
4
+ expect(subject.to_h).to eq(
5
+ 'count' => 0,
6
+ 'sum' => 0.0,
7
+ 'sumsq' => 0.0,
8
+ 'tdigest' => 'AAAAAkA0AAAAAAAAAAAAAA=='
9
+ )
10
+ end
11
+ end
12
+
13
+ describe "#increment" do
14
+ let(:start_time) { Time.new(2018, 1, 1, 0, 0, 20, 0) }
15
+ let(:end_time) { Time.new(2018, 1, 1, 0, 0, 22, 0) }
16
+
17
+ before { subject.increment(start_time, end_time) }
18
+
19
+ its(:sum) { is_expected.to eq(2000) }
20
+ end
21
+
22
+ describe "#increment_ms" do
23
+ before { subject.increment_ms(1000) }
24
+
25
+ its(:count) { is_expected.to eq(1) }
26
+ its(:sum) { is_expected.to eq(1000) }
27
+ its(:sumsq) { is_expected.to eq(1000000) }
28
+
29
+ it "updates tdigest" do
30
+ expect(subject.tdigest.size).to eq(1)
31
+ end
32
+ end
33
+
34
+ describe "#inspect" do
35
+ it "provides custom inspect output" do
36
+ expect(subject.inspect).to eq(
37
+ '#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>'
38
+ )
39
+ end
40
+ end
41
+
42
+ describe "#pretty_print" do
43
+ it "is an alias of #inspect" do
44
+ expect(subject.method(:pretty_print)).to eql(subject.method(:inspect))
45
+ end
46
+ end
47
+ end