dolores_rpm 3.2.0.2
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/CHANGELOG +559 -0
- data/LICENSE +64 -0
- data/README.rdoc +179 -0
- data/bin/mongrel_rpm +33 -0
- data/bin/newrelic +13 -0
- data/bin/newrelic_cmd +5 -0
- data/cert/cacert.pem +118 -0
- data/cert/oldsite.pem +28 -0
- data/cert/site.pem +27 -0
- data/dolores_rpm-3.3.4.fork.gem +0 -0
- data/install.rb +9 -0
- data/lib/conditional_vendored_dependency_detection.rb +3 -0
- data/lib/conditional_vendored_metric_parser.rb +5 -0
- data/lib/new_relic/agent/agent.rb +1311 -0
- data/lib/new_relic/agent/beacon_configuration.rb +110 -0
- data/lib/new_relic/agent/browser_monitoring.rb +102 -0
- data/lib/new_relic/agent/busy_calculator.rb +99 -0
- data/lib/new_relic/agent/chained_call.rb +13 -0
- data/lib/new_relic/agent/database.rb +203 -0
- data/lib/new_relic/agent/error_collector.rb +251 -0
- data/lib/new_relic/agent/instrumentation/active_merchant.rb +27 -0
- data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +68 -0
- data/lib/new_relic/agent/instrumentation/authlogic.rb +19 -0
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +424 -0
- data/lib/new_relic/agent/instrumentation/data_mapper.rb +57 -0
- data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +52 -0
- data/lib/new_relic/agent/instrumentation/memcache.rb +80 -0
- data/lib/new_relic/agent/instrumentation/merb/controller.rb +41 -0
- data/lib/new_relic/agent/instrumentation/merb/errors.rb +29 -0
- data/lib/new_relic/agent/instrumentation/metric_frame/pop.rb +80 -0
- data/lib/new_relic/agent/instrumentation/metric_frame.rb +332 -0
- data/lib/new_relic/agent/instrumentation/net.rb +29 -0
- data/lib/new_relic/agent/instrumentation/passenger_instrumentation.rb +36 -0
- data/lib/new_relic/agent/instrumentation/queue_time.rb +210 -0
- data/lib/new_relic/agent/instrumentation/rack.rb +98 -0
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +114 -0
- data/lib/new_relic/agent/instrumentation/rails/action_web_service.rb +42 -0
- data/lib/new_relic/agent/instrumentation/rails/active_record_instrumentation.rb +115 -0
- data/lib/new_relic/agent/instrumentation/rails/errors.rb +42 -0
- data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +118 -0
- data/lib/new_relic/agent/instrumentation/rails3/active_record_instrumentation.rb +122 -0
- data/lib/new_relic/agent/instrumentation/rails3/errors.rb +37 -0
- data/lib/new_relic/agent/instrumentation/sinatra.rb +58 -0
- data/lib/new_relic/agent/instrumentation/sunspot.rb +29 -0
- data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +21 -0
- data/lib/new_relic/agent/instrumentation.rb +9 -0
- data/lib/new_relic/agent/method_tracer.rb +528 -0
- data/lib/new_relic/agent/sampler.rb +50 -0
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +58 -0
- data/lib/new_relic/agent/samplers/delayed_job_lock_sampler.rb +40 -0
- data/lib/new_relic/agent/samplers/memory_sampler.rb +144 -0
- data/lib/new_relic/agent/samplers/object_sampler.rb +26 -0
- data/lib/new_relic/agent/shim_agent.rb +29 -0
- data/lib/new_relic/agent/sql_sampler.rb +267 -0
- data/lib/new_relic/agent/stats_engine/metric_stats.rb +187 -0
- data/lib/new_relic/agent/stats_engine/samplers.rb +95 -0
- data/lib/new_relic/agent/stats_engine/transactions.rb +208 -0
- data/lib/new_relic/agent/stats_engine.rb +25 -0
- data/lib/new_relic/agent/transaction_sample_builder.rb +101 -0
- data/lib/new_relic/agent/transaction_sampler.rb +397 -0
- data/lib/new_relic/agent/worker_loop.rb +89 -0
- data/lib/new_relic/agent.rb +454 -0
- data/lib/new_relic/collection_helper.rb +75 -0
- data/lib/new_relic/command.rb +85 -0
- data/lib/new_relic/commands/deployments.rb +105 -0
- data/lib/new_relic/commands/install.rb +80 -0
- data/lib/new_relic/control/class_methods.rb +53 -0
- data/lib/new_relic/control/configuration.rb +202 -0
- data/lib/new_relic/control/frameworks/external.rb +16 -0
- data/lib/new_relic/control/frameworks/merb.rb +31 -0
- data/lib/new_relic/control/frameworks/rails.rb +164 -0
- data/lib/new_relic/control/frameworks/rails3.rb +75 -0
- data/lib/new_relic/control/frameworks/ruby.rb +42 -0
- data/lib/new_relic/control/frameworks/sinatra.rb +20 -0
- data/lib/new_relic/control/frameworks.rb +10 -0
- data/lib/new_relic/control/instance_methods.rb +179 -0
- data/lib/new_relic/control/instrumentation.rb +100 -0
- data/lib/new_relic/control/logging_methods.rb +143 -0
- data/lib/new_relic/control/profiling.rb +25 -0
- data/lib/new_relic/control/server_methods.rb +114 -0
- data/lib/new_relic/control.rb +46 -0
- data/lib/new_relic/data_serialization.rb +157 -0
- data/lib/new_relic/delayed_job_injection.rb +46 -0
- data/lib/new_relic/language_support.rb +69 -0
- data/lib/new_relic/local_environment.rb +414 -0
- data/lib/new_relic/merbtasks.rb +6 -0
- data/lib/new_relic/metric_data.rb +51 -0
- data/lib/new_relic/metric_spec.rb +75 -0
- data/lib/new_relic/metrics.rb +9 -0
- data/lib/new_relic/noticed_error.rb +24 -0
- data/lib/new_relic/rack/browser_monitoring.rb +68 -0
- data/lib/new_relic/rack/developer_mode.rb +268 -0
- data/lib/new_relic/recipes.rb +73 -0
- data/lib/new_relic/stats.rb +388 -0
- data/lib/new_relic/timer_lib.rb +27 -0
- data/lib/new_relic/transaction_analysis/segment_summary.rb +49 -0
- data/lib/new_relic/transaction_analysis.rb +77 -0
- data/lib/new_relic/transaction_sample/composite_segment.rb +27 -0
- data/lib/new_relic/transaction_sample/fake_segment.rb +9 -0
- data/lib/new_relic/transaction_sample/segment.rb +201 -0
- data/lib/new_relic/transaction_sample/summary_segment.rb +21 -0
- data/lib/new_relic/transaction_sample.rb +245 -0
- data/lib/new_relic/url_rule.rb +14 -0
- data/lib/new_relic/version.rb +55 -0
- data/lib/newrelic_rpm.rb +49 -0
- data/lib/tasks/all.rb +4 -0
- data/lib/tasks/install.rake +7 -0
- data/lib/tasks/tests.rake +19 -0
- data/newrelic.yml +265 -0
- data/recipes/newrelic.rb +6 -0
- data/test/active_record_fixtures.rb +77 -0
- data/test/config/newrelic.yml +48 -0
- data/test/config/test_control.rb +48 -0
- data/test/new_relic/agent/agent/connect_test.rb +410 -0
- data/test/new_relic/agent/agent/start_test.rb +255 -0
- data/test/new_relic/agent/agent/start_worker_thread_test.rb +153 -0
- data/test/new_relic/agent/agent_test.rb +139 -0
- data/test/new_relic/agent/agent_test_controller.rb +77 -0
- data/test/new_relic/agent/agent_test_controller_test.rb +363 -0
- data/test/new_relic/agent/apdex_from_server_test.rb +9 -0
- data/test/new_relic/agent/beacon_configuration_test.rb +108 -0
- data/test/new_relic/agent/browser_monitoring_test.rb +278 -0
- data/test/new_relic/agent/busy_calculator_test.rb +81 -0
- data/test/new_relic/agent/database_test.rb +162 -0
- data/test/new_relic/agent/error_collector/notice_error_test.rb +257 -0
- data/test/new_relic/agent/error_collector_test.rb +175 -0
- data/test/new_relic/agent/instrumentation/active_record_instrumentation_test.rb +538 -0
- data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +36 -0
- data/test/new_relic/agent/instrumentation/instrumentation_test.rb +11 -0
- data/test/new_relic/agent/instrumentation/metric_frame/pop_test.rb +172 -0
- data/test/new_relic/agent/instrumentation/metric_frame_test.rb +50 -0
- data/test/new_relic/agent/instrumentation/net_instrumentation_test.rb +84 -0
- data/test/new_relic/agent/instrumentation/queue_time_test.rb +387 -0
- data/test/new_relic/agent/instrumentation/rack_test.rb +35 -0
- data/test/new_relic/agent/instrumentation/task_instrumentation_test.rb +184 -0
- data/test/new_relic/agent/memcache_instrumentation_test.rb +143 -0
- data/test/new_relic/agent/method_tracer/class_methods/add_method_tracer_test.rb +164 -0
- data/test/new_relic/agent/method_tracer/instance_methods/trace_execution_scoped_test.rb +234 -0
- data/test/new_relic/agent/method_tracer_test.rb +386 -0
- data/test/new_relic/agent/mock_scope_listener.rb +23 -0
- data/test/new_relic/agent/rpm_agent_test.rb +149 -0
- data/test/new_relic/agent/sampler_test.rb +19 -0
- data/test/new_relic/agent/shim_agent_test.rb +20 -0
- data/test/new_relic/agent/sql_sampler_test.rb +160 -0
- data/test/new_relic/agent/stats_engine/metric_stats/harvest_test.rb +150 -0
- data/test/new_relic/agent/stats_engine/metric_stats_test.rb +82 -0
- data/test/new_relic/agent/stats_engine/samplers_test.rb +99 -0
- data/test/new_relic/agent/stats_engine_test.rb +185 -0
- data/test/new_relic/agent/transaction_sample_builder_test.rb +195 -0
- data/test/new_relic/agent/transaction_sampler_test.rb +955 -0
- data/test/new_relic/agent/worker_loop_test.rb +66 -0
- data/test/new_relic/agent_test.rb +175 -0
- data/test/new_relic/collection_helper_test.rb +149 -0
- data/test/new_relic/command/deployments_test.rb +68 -0
- data/test/new_relic/control/class_methods_test.rb +62 -0
- data/test/new_relic/control/configuration_test.rb +72 -0
- data/test/new_relic/control/logging_methods_test.rb +185 -0
- data/test/new_relic/control_test.rb +254 -0
- data/test/new_relic/data_serialization_test.rb +208 -0
- data/test/new_relic/delayed_job_injection_test.rb +16 -0
- data/test/new_relic/local_environment_test.rb +72 -0
- data/test/new_relic/metric_data_test.rb +125 -0
- data/test/new_relic/metric_spec_test.rb +95 -0
- data/test/new_relic/rack/all_test.rb +11 -0
- data/test/new_relic/rack/browser_monitoring_test.rb +84 -0
- data/test/new_relic/rack/developer_mode_helper_test.rb +141 -0
- data/test/new_relic/rack/developer_mode_test.rb +43 -0
- data/test/new_relic/stats_test.rb +426 -0
- data/test/new_relic/transaction_analysis/segment_summary_test.rb +91 -0
- data/test/new_relic/transaction_analysis_test.rb +121 -0
- data/test/new_relic/transaction_sample/composite_segment_test.rb +35 -0
- data/test/new_relic/transaction_sample/fake_segment_test.rb +17 -0
- data/test/new_relic/transaction_sample/segment_test.rb +389 -0
- data/test/new_relic/transaction_sample/summary_segment_test.rb +31 -0
- data/test/new_relic/transaction_sample_subtest_test.rb +56 -0
- data/test/new_relic/transaction_sample_test.rb +164 -0
- data/test/new_relic/version_number_test.rb +89 -0
- data/test/test_contexts.rb +29 -0
- data/test/test_helper.rb +154 -0
- data/ui/helpers/developer_mode_helper.rb +357 -0
- data/ui/helpers/google_pie_chart.rb +48 -0
- data/ui/views/layouts/newrelic_default.rhtml +47 -0
- data/ui/views/newrelic/_explain_plans.rhtml +27 -0
- data/ui/views/newrelic/_sample.rhtml +20 -0
- data/ui/views/newrelic/_segment.rhtml +28 -0
- data/ui/views/newrelic/_segment_limit_message.rhtml +1 -0
- data/ui/views/newrelic/_segment_row.rhtml +12 -0
- data/ui/views/newrelic/_show_sample_detail.rhtml +24 -0
- data/ui/views/newrelic/_show_sample_sql.rhtml +24 -0
- data/ui/views/newrelic/_show_sample_summary.rhtml +3 -0
- data/ui/views/newrelic/_sql_row.rhtml +16 -0
- data/ui/views/newrelic/_stack_trace.rhtml +15 -0
- data/ui/views/newrelic/_table.rhtml +12 -0
- data/ui/views/newrelic/explain_sql.rhtml +43 -0
- data/ui/views/newrelic/file/images/arrow-close.png +0 -0
- data/ui/views/newrelic/file/images/arrow-open.png +0 -0
- data/ui/views/newrelic/file/images/blue_bar.gif +0 -0
- data/ui/views/newrelic/file/images/file_icon.png +0 -0
- data/ui/views/newrelic/file/images/gray_bar.gif +0 -0
- data/ui/views/newrelic/file/images/new-relic-rpm-desktop.gif +0 -0
- data/ui/views/newrelic/file/images/new_relic_rpm_desktop.gif +0 -0
- data/ui/views/newrelic/file/images/textmate.png +0 -0
- data/ui/views/newrelic/file/javascript/jquery-1.4.2.js +6240 -0
- data/ui/views/newrelic/file/javascript/transaction_sample.js +120 -0
- data/ui/views/newrelic/file/stylesheets/style.css +490 -0
- data/ui/views/newrelic/index.rhtml +71 -0
- data/ui/views/newrelic/sample_not_found.rhtml +2 -0
- data/ui/views/newrelic/show_sample.rhtml +80 -0
- data/ui/views/newrelic/show_source.rhtml +3 -0
- data/ui/views/newrelic/threads.rhtml +53 -0
- data/vendor/gems/dependency_detection-0.0.1.build/LICENSE +5 -0
- data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection/version.rb +3 -0
- data/vendor/gems/dependency_detection-0.0.1.build/lib/dependency_detection.rb +62 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/metric_parser.rb +1 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/action_mailer.rb +14 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_merchant.rb +31 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/active_record.rb +33 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/apdex.rb +89 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/background_transaction.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/client.rb +46 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller.rb +67 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_cpu.rb +43 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/controller_ext.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database.rb +48 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/database_pool.rb +24 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net.rb +28 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/dot_net_parser.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/errors.rb +11 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/external.rb +55 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/frontend.rb +40 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/gc.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/hibernate_session.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java.rb +31 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/java_parser.rb +17 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp.rb +34 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/jsp_tag.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/mem_cache.rb +55 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/metric_parser.rb +122 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/orm.rb +27 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/other_transaction.rb +40 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_context_listener.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/servlet_filter.rb +7 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr.rb +27 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/solr_request_handler.rb +15 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring.rb +54 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_controller.rb +6 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/spring_view.rb +6 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_action.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/struts_result.rb +20 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/version.rb +5 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/view.rb +70 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_frontend.rb +18 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_service.rb +14 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser/web_transaction.rb +133 -0
- data/vendor/gems/metric_parser-0.1.0.pre1/lib/new_relic/metric_parser.rb +64 -0
- metadata +398 -0
|
@@ -0,0 +1,955 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'..','..','test_helper'))
|
|
2
|
+
|
|
3
|
+
class NewRelic::Agent::TransactionSamplerTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
module MockGCStats
|
|
6
|
+
|
|
7
|
+
def time
|
|
8
|
+
return 0 if @@values.empty?
|
|
9
|
+
raise "too many calls" if @@index >= @@values.size
|
|
10
|
+
@@curtime ||= 0
|
|
11
|
+
@@curtime += (@@values[@@index] * 1e09).to_i
|
|
12
|
+
@@index += 1
|
|
13
|
+
@@curtime
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.mock_values= array
|
|
17
|
+
@@values = array
|
|
18
|
+
@@index = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def setup
|
|
24
|
+
Thread::current[:record_sql] = nil
|
|
25
|
+
agent = NewRelic::Agent.instance
|
|
26
|
+
stats_engine = NewRelic::Agent::StatsEngine.new
|
|
27
|
+
agent.stubs(:stats_engine).returns(stats_engine)
|
|
28
|
+
@sampler = NewRelic::Agent::TransactionSampler.new
|
|
29
|
+
stats_engine.transaction_sampler = @sampler
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def teardown
|
|
33
|
+
super
|
|
34
|
+
Thread.current[:transaction_sample_builder] = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_initialize
|
|
38
|
+
defaults = {
|
|
39
|
+
:samples => [],
|
|
40
|
+
:harvest_count => 0,
|
|
41
|
+
:max_samples => 100,
|
|
42
|
+
:random_sample => nil,
|
|
43
|
+
}
|
|
44
|
+
defaults.each do |variable, default_value|
|
|
45
|
+
assert_equal(default_value, @sampler.instance_variable_get('@' + variable.to_s))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
segment_limit = @sampler.instance_variable_get('@segment_limit')
|
|
49
|
+
assert(segment_limit.is_a?(Numeric), "Segment limit should be numeric")
|
|
50
|
+
assert(segment_limit > 0, "Segment limit should be above zero")
|
|
51
|
+
|
|
52
|
+
stack_trace_threshold = @sampler.instance_variable_get('@stack_trace_threshold')
|
|
53
|
+
assert(stack_trace_threshold.is_a?((0.1).class), "Stack trace threshold should be a #{(0.1).class.inspect}, but is #{stack_trace_threshold.inspect}")
|
|
54
|
+
assert(stack_trace_threshold > 0.0, "Stack trace threshold should be above zero")
|
|
55
|
+
|
|
56
|
+
lock = @sampler.instance_variable_get('@samples_lock')
|
|
57
|
+
assert(lock.is_a?(Mutex), "Samples lock should be a mutex, is: #{lock.inspect}")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_configure
|
|
61
|
+
control = NewRelic::Control.instance
|
|
62
|
+
control.merge_options('transaction_tracer' => {'stack_trace_threshold' => 5.0, 'limit_segments' => 20, 'explain_threshold' => 4.0})
|
|
63
|
+
@sampler.configure!
|
|
64
|
+
assert_equal 20, @sampler.instance_variable_get('@segment_limit')
|
|
65
|
+
assert_equal 5.0, @sampler.instance_variable_get('@stack_trace_threshold')
|
|
66
|
+
assert_equal 4.0, @sampler.instance_variable_get('@explain_threshold')
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_current_sample_id_default
|
|
70
|
+
builder = mock('builder')
|
|
71
|
+
builder.expects(:sample_id).returns(11111)
|
|
72
|
+
@sampler.expects(:builder).returns(builder)
|
|
73
|
+
assert_equal(11111, @sampler.current_sample_id)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_current_sample_id_no_builder
|
|
77
|
+
@sampler.expects(:builder).returns(nil)
|
|
78
|
+
assert_equal(nil, @sampler.current_sample_id)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_enable
|
|
82
|
+
assert_equal(nil, @sampler.instance_variable_get('@disabled'))
|
|
83
|
+
@sampler.enable
|
|
84
|
+
assert_equal(false, @sampler.instance_variable_get('@disabled'))
|
|
85
|
+
assert_equal(@sampler, NewRelic::Agent.instance.stats_engine.instance_variable_get('@transaction_sampler'))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_disable
|
|
89
|
+
assert_nil @sampler.instance_variable_get('@disabled')
|
|
90
|
+
@sampler.disable
|
|
91
|
+
assert @sampler.instance_variable_get('@disabled')
|
|
92
|
+
assert_nil NewRelic::Agent.instance.stats_engine.instance_variable_get('@transaction_sampler')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_sampling_rate_equals_default
|
|
96
|
+
@sampler.sampling_rate = 1
|
|
97
|
+
assert_equal(1, @sampler.instance_variable_get('@sampling_rate'))
|
|
98
|
+
# rand(1) is always zero, so we can be sure here
|
|
99
|
+
assert_equal(0, @sampler.instance_variable_get('@harvest_count'))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_sampling_rate_equals_with_a_float
|
|
103
|
+
@sampler.sampling_rate = 5.5
|
|
104
|
+
assert_equal(5, @sampler.instance_variable_get('@sampling_rate'))
|
|
105
|
+
harvest_count = @sampler.instance_variable_get('@harvest_count')
|
|
106
|
+
assert((0..4).include?(harvest_count), "should be in the range 0..4")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_notice_first_scope_push_default
|
|
110
|
+
@sampler.expects(:disabled).returns(false)
|
|
111
|
+
@sampler.expects(:start_builder).with(100.0)
|
|
112
|
+
@sampler.notice_first_scope_push(Time.at(100))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_notice_first_scope_push_disabled
|
|
116
|
+
@sampler.expects(:disabled).returns(true)
|
|
117
|
+
@sampler.expects(:start_builder).never
|
|
118
|
+
@sampler.notice_first_scope_push(Time.at(100))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_notice_push_scope_no_builder
|
|
122
|
+
@sampler.expects(:builder)
|
|
123
|
+
assert_equal(nil, @sampler.notice_push_scope('a scope'))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_notice_push_scope_with_builder
|
|
127
|
+
NewRelic::Control.instance.expects(:developer_mode?).returns(false)
|
|
128
|
+
builder = mock('builder')
|
|
129
|
+
builder.expects(:trace_entry).with('a scope', 100.0)
|
|
130
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
131
|
+
|
|
132
|
+
@sampler.notice_push_scope('a scope', Time.at(100))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def test_notice_push_scope_in_dev_mode
|
|
136
|
+
NewRelic::Control.instance.expects(:developer_mode?).returns(true)
|
|
137
|
+
|
|
138
|
+
builder = mock('builder')
|
|
139
|
+
builder.expects(:trace_entry).with('a scope', 100.0)
|
|
140
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
141
|
+
@sampler.expects(:capture_segment_trace)
|
|
142
|
+
|
|
143
|
+
@sampler.notice_push_scope('a scope', Time.at(100))
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_scope_depth_no_builder
|
|
147
|
+
@sampler.expects(:builder).returns(nil)
|
|
148
|
+
assert_equal(0, @sampler.scope_depth, "should default to zero with no builder")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_scope_depth_with_builder
|
|
152
|
+
builder = mock('builder')
|
|
153
|
+
builder.expects(:scope_depth).returns('scope_depth')
|
|
154
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
155
|
+
|
|
156
|
+
assert_equal('scope_depth', @sampler.scope_depth, "should delegate scope depth to the builder")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_notice_pop_scope_no_builder
|
|
160
|
+
@sampler.expects(:builder).returns(nil)
|
|
161
|
+
assert_equal(nil, @sampler.notice_pop_scope('a scope', Time.at(100)))
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_notice_pop_scope_with_frozen_sample
|
|
165
|
+
builder = mock('builder')
|
|
166
|
+
sample = mock('sample')
|
|
167
|
+
builder.expects(:sample).returns(sample)
|
|
168
|
+
sample.expects(:frozen?).returns(true)
|
|
169
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
170
|
+
|
|
171
|
+
assert_raise(RuntimeError) do
|
|
172
|
+
@sampler.notice_pop_scope('a scope', Time.at(100))
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def test_notice_pop_scope_builder_delegation
|
|
177
|
+
builder = mock('builder')
|
|
178
|
+
builder.expects(:trace_exit).with('a scope', 100.0)
|
|
179
|
+
sample = mock('sample')
|
|
180
|
+
builder.expects(:sample).returns(sample)
|
|
181
|
+
sample.expects(:frozen?).returns(false)
|
|
182
|
+
@sampler.expects(:builder).returns(builder).times(3)
|
|
183
|
+
|
|
184
|
+
@sampler.notice_pop_scope('a scope', Time.at(100))
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_notice_scope_empty_no_builder
|
|
188
|
+
@sampler.expects(:builder).returns(nil)
|
|
189
|
+
assert_equal(nil, @sampler.notice_scope_empty)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_notice_scope_empty_ignored_transaction
|
|
193
|
+
builder = mock('builder')
|
|
194
|
+
# the builder should be cached, so only called once
|
|
195
|
+
@sampler.expects(:builder).returns(builder).once
|
|
196
|
+
|
|
197
|
+
builder.expects(:finish_trace).with(100.0)
|
|
198
|
+
|
|
199
|
+
@sampler.expects(:clear_builder)
|
|
200
|
+
|
|
201
|
+
builder.expects(:ignored?).returns(true)
|
|
202
|
+
|
|
203
|
+
assert_equal(nil, @sampler.notice_scope_empty(Time.at(100)))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_notice_scope_empty_with_builder
|
|
207
|
+
builder = mock('builder')
|
|
208
|
+
# the builder should be cached, so only called once
|
|
209
|
+
@sampler.expects(:builder).returns(builder).once
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
builder.expects(:finish_trace).with(100.0)
|
|
213
|
+
@sampler.expects(:clear_builder)
|
|
214
|
+
|
|
215
|
+
builder.expects(:ignored?).returns(false)
|
|
216
|
+
|
|
217
|
+
sample = mock('sample')
|
|
218
|
+
builder.expects(:sample).returns(sample)
|
|
219
|
+
@sampler.expects(:store_sample).with(sample)
|
|
220
|
+
|
|
221
|
+
@sampler.notice_scope_empty(Time.at(100))
|
|
222
|
+
|
|
223
|
+
assert_equal(sample, @sampler.instance_variable_get('@last_sample'))
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def test_store_random_sample_no_random_sampling
|
|
227
|
+
@sampler.instance_eval { @random_sampling = false }
|
|
228
|
+
assert_equal(nil, @sampler.instance_variable_get('@random_sample'))
|
|
229
|
+
@sampler.store_random_sample(mock('sample'))
|
|
230
|
+
assert_equal(nil, @sampler.instance_variable_get('@random_sample'))
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def test_store_random_sample_random_sampling
|
|
234
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
235
|
+
sample = mock('sample')
|
|
236
|
+
assert_equal(nil, @sampler.instance_variable_get('@random_sample'))
|
|
237
|
+
@sampler.store_random_sample(sample)
|
|
238
|
+
assert_equal(sample, @sampler.instance_variable_get('@random_sample'))
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_store_sample_for_developer_mode_in_dev_mode
|
|
242
|
+
NewRelic::Control.instance.expects(:developer_mode?).returns(true)
|
|
243
|
+
sample = mock('sample')
|
|
244
|
+
@sampler.expects(:truncate_samples)
|
|
245
|
+
@sampler.store_sample_for_developer_mode(sample)
|
|
246
|
+
assert_equal([sample], @sampler.instance_variable_get('@samples'))
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def test_store_sample_for_developer_mode_no_dev
|
|
250
|
+
NewRelic::Control.instance.expects(:developer_mode?).returns(false)
|
|
251
|
+
sample = mock('sample')
|
|
252
|
+
@sampler.store_sample_for_developer_mode(sample)
|
|
253
|
+
assert_equal([], @sampler.instance_variable_get('@samples'))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def test_store_slowest_sample_new_is_slowest
|
|
257
|
+
old_sample = mock('old_sample')
|
|
258
|
+
new_sample = mock('new_sample')
|
|
259
|
+
@sampler.instance_eval { @slowest_sample = old_sample }
|
|
260
|
+
@sampler.expects(:slowest_sample?).with(old_sample, new_sample).returns(true)
|
|
261
|
+
|
|
262
|
+
@sampler.store_slowest_sample(new_sample)
|
|
263
|
+
|
|
264
|
+
assert_equal(new_sample, @sampler.instance_variable_get('@slowest_sample'))
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_store_slowest_sample_not_slowest
|
|
269
|
+
old_sample = mock('old_sample')
|
|
270
|
+
new_sample = mock('new_sample')
|
|
271
|
+
@sampler.instance_eval { @slowest_sample = old_sample }
|
|
272
|
+
@sampler.expects(:slowest_sample?).with(old_sample, new_sample).returns(false)
|
|
273
|
+
|
|
274
|
+
@sampler.store_slowest_sample(new_sample)
|
|
275
|
+
|
|
276
|
+
assert_equal(old_sample, @sampler.instance_variable_get('@slowest_sample'))
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def test_slowest_sample_no_sample
|
|
280
|
+
old_sample = nil
|
|
281
|
+
new_sample = mock('new_sample')
|
|
282
|
+
assert_equal(true, @sampler.slowest_sample?(old_sample, new_sample))
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def test_slowest_sample_faster_sample
|
|
286
|
+
old_sample = mock('old_sample')
|
|
287
|
+
new_sample = mock('new_sample')
|
|
288
|
+
old_sample.expects(:duration).returns(1.0)
|
|
289
|
+
new_sample.expects(:duration).returns(0.5)
|
|
290
|
+
assert_equal(false, @sampler.slowest_sample?(old_sample, new_sample))
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def test_slowest_sample_slower_sample
|
|
294
|
+
old_sample = mock('old_sample')
|
|
295
|
+
new_sample = mock('new_sample')
|
|
296
|
+
old_sample.expects(:duration).returns(0.5)
|
|
297
|
+
new_sample.expects(:duration).returns(1.0)
|
|
298
|
+
assert_equal(true, @sampler.slowest_sample?(old_sample, new_sample))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def test_truncate_samples_no_samples
|
|
302
|
+
@sampler.instance_eval { @max_samples = 10 }
|
|
303
|
+
@sampler.instance_eval { @samples = [] }
|
|
304
|
+
@sampler.truncate_samples
|
|
305
|
+
assert_equal([], @sampler.instance_variable_get('@samples'))
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def test_truncate_samples_equal_samples
|
|
309
|
+
@sampler.instance_eval { @max_samples = 2 }
|
|
310
|
+
@sampler.instance_eval { @samples = [1, 2] }
|
|
311
|
+
@sampler.truncate_samples
|
|
312
|
+
assert_equal([1, 2], @sampler.instance_variable_get('@samples'))
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def test_truncate_samples_extra_samples
|
|
316
|
+
@sampler.instance_eval { @max_samples = 2 }
|
|
317
|
+
@sampler.instance_eval { @samples = [1, 2, 3] }
|
|
318
|
+
@sampler.truncate_samples
|
|
319
|
+
assert_equal([2, 3], @sampler.instance_variable_get('@samples'))
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def test_notice_transaction_disabled
|
|
323
|
+
@sampler.expects(:disabled).returns(true)
|
|
324
|
+
@sampler.expects(:builder).never # since we're disabled
|
|
325
|
+
@sampler.notice_transaction('foo')
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def test_notice_transaction_no_builder
|
|
329
|
+
@sampler.expects(:disabled).returns(false)
|
|
330
|
+
@sampler.expects(:builder).returns(nil).once
|
|
331
|
+
@sampler.notice_transaction('foo')
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def test_notice_transaction_with_builder
|
|
335
|
+
builder = mock('builder')
|
|
336
|
+
builder.expects(:set_transaction_info).with('a path', 'a uri', {:some => :params})
|
|
337
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
338
|
+
@sampler.expects(:disabled).returns(false)
|
|
339
|
+
@sampler.notice_transaction('a path', 'a uri', {:some => :params})
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def test_ignore_transaction_no_builder
|
|
343
|
+
@sampler.expects(:builder).returns(nil).once
|
|
344
|
+
@sampler.ignore_transaction
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def test_ignore_transaction_with_builder
|
|
348
|
+
builder = mock('builder')
|
|
349
|
+
builder.expects(:ignore_transaction)
|
|
350
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
351
|
+
@sampler.ignore_transaction
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def test_notice_profile_no_builder
|
|
355
|
+
@sampler.expects(:builder).returns(nil).once
|
|
356
|
+
@sampler.notice_profile(nil)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def test_notice_profile_with_builder
|
|
360
|
+
profile = mock('profile')
|
|
361
|
+
builder = mock('builder')
|
|
362
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
363
|
+
builder.expects(:set_profile).with(profile)
|
|
364
|
+
|
|
365
|
+
@sampler.notice_profile(profile)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def test_notice_transaction_cpu_time_no_builder
|
|
369
|
+
@sampler.expects(:builder).returns(nil).once
|
|
370
|
+
@sampler.notice_transaction_cpu_time(0.0)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def test_notice_transaction_cpu_time_with_builder
|
|
374
|
+
cpu_time = mock('cpu_time')
|
|
375
|
+
builder = mock('builder')
|
|
376
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
377
|
+
builder.expects(:set_transaction_cpu_time).with(cpu_time)
|
|
378
|
+
|
|
379
|
+
@sampler.notice_transaction_cpu_time(cpu_time)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def test_notice_extra_data_no_builder
|
|
383
|
+
@sampler.expects(:builder).returns(nil).once
|
|
384
|
+
@sampler.send(:notice_extra_data, nil, nil, nil)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def test_notice_extra_data_no_segment
|
|
388
|
+
builder = mock('builder')
|
|
389
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
390
|
+
builder.expects(:current_segment).returns(nil)
|
|
391
|
+
@sampler.send(:notice_extra_data, nil, nil, nil)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def test_notice_extra_data_with_segment_no_old_message_no_config_key
|
|
395
|
+
key = :a_key
|
|
396
|
+
builder = mock('builder')
|
|
397
|
+
segment = mock('segment')
|
|
398
|
+
@sampler.expects(:builder).returns(builder).twice
|
|
399
|
+
builder.expects(:current_segment).returns(segment)
|
|
400
|
+
segment.expects(:[]).with(key).returns(nil)
|
|
401
|
+
@sampler.expects(:append_new_message).with(nil, 'a message').returns('a message')
|
|
402
|
+
@sampler.expects(:truncate_message).with('a message').returns('truncated_message')
|
|
403
|
+
segment.expects(:[]=).with(key, 'truncated_message')
|
|
404
|
+
@sampler.expects(:append_backtrace).with(segment, 1.0)
|
|
405
|
+
@sampler.send(:notice_extra_data, 'a message', 1.0, key)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def test_truncate_message_short_message
|
|
409
|
+
message = 'a message'
|
|
410
|
+
assert_equal(message, @sampler.truncate_message(message))
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def test_truncate_message_long_message
|
|
414
|
+
message = 'a' * 16384
|
|
415
|
+
truncated_message = @sampler.truncate_message(message)
|
|
416
|
+
assert_equal(16384, truncated_message.length)
|
|
417
|
+
assert_equal('a' * 16381 + '...', truncated_message)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def test_append_new_message_no_old_message
|
|
421
|
+
old_message = nil
|
|
422
|
+
new_message = 'a message'
|
|
423
|
+
assert_equal(new_message, @sampler.append_new_message(old_message, new_message))
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def test_append_new_message_with_old_message
|
|
427
|
+
old_message = 'old message'
|
|
428
|
+
new_message = ' a message'
|
|
429
|
+
assert_equal("old message;\n a message", @sampler.append_new_message(old_message, new_message))
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def test_append_backtrace_under_duration
|
|
433
|
+
@sampler.instance_eval { @stack_trace_threshold = 2.0 }
|
|
434
|
+
segment = mock('segment')
|
|
435
|
+
segment.expects(:[]=).with(:backtrace, any_parameters).never
|
|
436
|
+
@sampler.append_backtrace(mock('segment'), 1.0)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def test_append_backtrace_over_duration
|
|
440
|
+
@sampler.instance_eval { @stack_trace_threshold = 2.0 }
|
|
441
|
+
segment = mock('segment')
|
|
442
|
+
# note the mocha expectation matcher - you can't hardcode a
|
|
443
|
+
# backtrace so we match on any string, which should be okay.
|
|
444
|
+
segment.expects(:[]=).with(:backtrace, instance_of(String))
|
|
445
|
+
@sampler.append_backtrace(segment, 2.5)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def test_notice_sql_recording_sql
|
|
449
|
+
Thread.current[:record_sql] = true
|
|
450
|
+
@sampler.expects(:notice_extra_data).with('some sql', 1.0, :sql, 'a config', :connection_config)
|
|
451
|
+
@sampler.notice_sql('some sql', 'a config', 1.0)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def test_notice_sql_not_recording
|
|
455
|
+
Thread.current[:record_sql] = false
|
|
456
|
+
@sampler.expects(:notice_extra_data).with('some sql', 1.0, :sql, 'a config', :connection_config).never # <--- important
|
|
457
|
+
@sampler.notice_sql('some sql', 'a config', 1.0)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def test_notice_nosql
|
|
461
|
+
@sampler.expects(:notice_extra_data).with('a key', 1.0, :key)
|
|
462
|
+
@sampler.notice_nosql('a key', 1.0)
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def test_harvest_when_disabled
|
|
466
|
+
@sampler.expects(:disabled).returns(true)
|
|
467
|
+
assert_equal([], @sampler.harvest)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def test_harvest_defaults
|
|
471
|
+
# making sure the sampler clears out the old samples
|
|
472
|
+
@sampler.instance_eval do
|
|
473
|
+
@slowest_sample = 'a sample'
|
|
474
|
+
@random_sample = 'a sample'
|
|
475
|
+
@last_sample = 'a sample'
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
@sampler.expects(:disabled).returns(false)
|
|
479
|
+
@sampler.expects(:add_samples_to).with([], 2.0).returns([])
|
|
480
|
+
|
|
481
|
+
assert_equal([], @sampler.harvest)
|
|
482
|
+
|
|
483
|
+
# make sure the samples have been cleared
|
|
484
|
+
assert_equal(nil, @sampler.instance_variable_get('@slowest_sample'))
|
|
485
|
+
assert_equal(nil, @sampler.instance_variable_get('@random_sample'))
|
|
486
|
+
assert_equal(nil, @sampler.instance_variable_get('@last_sample'))
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
def test_harvest_with_previous_samples
|
|
490
|
+
sample = mock('sample')
|
|
491
|
+
@sampler.expects(:disabled).returns(false)
|
|
492
|
+
@sampler.expects(:add_samples_to).with([sample], 2.0).returns([sample])
|
|
493
|
+
@sampler.instance_eval { @segment_limit = 2000 }
|
|
494
|
+
sample.expects(:truncate).with(2000)
|
|
495
|
+
assert_equal([sample], @sampler.harvest([sample]))
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def test_add_random_sample_to_not_random_sampling
|
|
499
|
+
@sampler.instance_eval { @random_sampling = false }
|
|
500
|
+
result = []
|
|
501
|
+
@sampler.add_random_sample_to(result)
|
|
502
|
+
assert_equal([], result, "should not add anything to the array if we are not random sampling")
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def test_add_random_sample_to_no_random_sample
|
|
506
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
507
|
+
@sampler.instance_eval {
|
|
508
|
+
@harvest_count = 1
|
|
509
|
+
@sampling_rate = 2
|
|
510
|
+
@random_sample = nil
|
|
511
|
+
}
|
|
512
|
+
result = []
|
|
513
|
+
@sampler.add_random_sample_to(result)
|
|
514
|
+
assert_equal([], result, "should not add sample to the array when it is nil")
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def test_add_random_sample_to_not_active
|
|
518
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
519
|
+
sample = mock('sample')
|
|
520
|
+
@sampler.instance_eval {
|
|
521
|
+
@harvest_count = 4
|
|
522
|
+
@sampling_rate = 40 # 4 % 40 = 4, so the sample should not be added
|
|
523
|
+
@random_sample = sample
|
|
524
|
+
}
|
|
525
|
+
result = []
|
|
526
|
+
@sampler.add_random_sample_to(result)
|
|
527
|
+
assert_equal([], result, "should not add samples to the array when harvest count is not moduli sampling rate")
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def test_add_random_sample_to_duplicate
|
|
531
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
532
|
+
sample = mock('sample')
|
|
533
|
+
@sampler.instance_eval {
|
|
534
|
+
@harvest_count = 1
|
|
535
|
+
@sampling_rate = 2
|
|
536
|
+
@random_sample = sample
|
|
537
|
+
}
|
|
538
|
+
result = [sample]
|
|
539
|
+
@sampler.add_random_sample_to(result)
|
|
540
|
+
assert_equal([sample], result, "should not add duplicate samples to the array")
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def test_add_random_sample_to_activated
|
|
544
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
545
|
+
sample = mock('sample')
|
|
546
|
+
@sampler.instance_eval {
|
|
547
|
+
@harvest_count = 3
|
|
548
|
+
@sampling_rate = 1
|
|
549
|
+
@random_sample = sample
|
|
550
|
+
}
|
|
551
|
+
result = []
|
|
552
|
+
@sampler.add_random_sample_to(result)
|
|
553
|
+
assert_equal([sample], result, "should add the random sample to the array")
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def test_add_random_sample_to_sampling_rate_zero
|
|
557
|
+
@sampler.instance_eval { @random_sampling = true }
|
|
558
|
+
sample = mock('sample')
|
|
559
|
+
@sampler.instance_eval {
|
|
560
|
+
@harvest_count = 3
|
|
561
|
+
@sampling_rate = 0
|
|
562
|
+
@random_sample = sample
|
|
563
|
+
}
|
|
564
|
+
result = []
|
|
565
|
+
@sampler.add_random_sample_to(result)
|
|
566
|
+
assert_equal([], result, "should not add the sample to the array")
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def test_add_samples_to_no_data
|
|
571
|
+
result = []
|
|
572
|
+
slow_threshold = 2.0
|
|
573
|
+
@sampler.instance_eval { @slowest_sample = nil }
|
|
574
|
+
@sampler.expects(:add_random_sample_to).with([])
|
|
575
|
+
assert_equal([], @sampler.add_samples_to(result, slow_threshold))
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def test_add_samples_to_one_result
|
|
579
|
+
sample = mock('sample')
|
|
580
|
+
sample.expects(:duration).returns(1).at_least_once
|
|
581
|
+
result = [sample]
|
|
582
|
+
slow_threshold = 2.0
|
|
583
|
+
@sampler.instance_eval { @slowest_sample = nil }
|
|
584
|
+
@sampler.expects(:add_random_sample_to).with([sample])
|
|
585
|
+
assert_equal([sample], @sampler.add_samples_to(result, slow_threshold))
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def test_add_samples_to_adding_slowest
|
|
589
|
+
sample = mock('sample')
|
|
590
|
+
sample.expects(:duration).returns(2.5).at_least_once
|
|
591
|
+
result = []
|
|
592
|
+
slow_threshold = 2.0
|
|
593
|
+
@sampler.instance_eval { @slowest_sample = sample }
|
|
594
|
+
@sampler.expects(:add_random_sample_to).with([sample])
|
|
595
|
+
assert_equal([sample], @sampler.add_samples_to(result, slow_threshold))
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def test_add_samples_to_under_threshold
|
|
599
|
+
result = []
|
|
600
|
+
slow_threshold = 2.0
|
|
601
|
+
sample = mock('sample')
|
|
602
|
+
sample.expects(:duration).returns(1.0).at_least_once
|
|
603
|
+
@sampler.instance_eval { @slowest_sample = sample }
|
|
604
|
+
@sampler.expects(:add_random_sample_to).with([])
|
|
605
|
+
assert_equal([], @sampler.add_samples_to(result, slow_threshold))
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def test_add_samples_to_two_sample_enter_one_sample_leave
|
|
609
|
+
slower_sample = mock('slower')
|
|
610
|
+
slower_sample.expects(:duration).returns(10.0).at_least_once
|
|
611
|
+
faster_sample = mock('faster')
|
|
612
|
+
faster_sample.expects(:duration).returns(5.0).at_least_once
|
|
613
|
+
result = [faster_sample]
|
|
614
|
+
slow_threshold = 2.0
|
|
615
|
+
@sampler.instance_eval { @slowest_sample = slower_sample }
|
|
616
|
+
@sampler.expects(:add_random_sample_to).with([slower_sample])
|
|
617
|
+
assert_equal([slower_sample], @sampler.add_samples_to(result, slow_threshold))
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def test_add_samples_to_keep_older_slower_sample
|
|
621
|
+
slower_sample = mock('slower')
|
|
622
|
+
slower_sample.expects(:duration).returns(10.0).at_least_once
|
|
623
|
+
faster_sample = mock('faster')
|
|
624
|
+
faster_sample.expects(:duration).returns(5.0).at_least_once
|
|
625
|
+
result = [slower_sample]
|
|
626
|
+
slow_threshold = 2.0
|
|
627
|
+
@sampler.instance_eval { @slowest_sample = faster_sample }
|
|
628
|
+
@sampler.expects(:add_random_sample_to).with([slower_sample])
|
|
629
|
+
assert_equal([slower_sample], @sampler.add_samples_to(result, slow_threshold))
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def test_start_builder_default
|
|
633
|
+
Thread.current[:record_tt] = true
|
|
634
|
+
@sampler.expects(:disabled).returns(false)
|
|
635
|
+
NewRelic::Agent.expects(:is_execution_traced?).returns(true)
|
|
636
|
+
@sampler.send(:start_builder)
|
|
637
|
+
assert(Thread.current[:transaction_sample_builder].is_a?(NewRelic::Agent::TransactionSampleBuilder), "should set up a new builder by default")
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
def test_start_builder_disabled
|
|
641
|
+
Thread.current[:transaction_sample_builder] = 'not nil.'
|
|
642
|
+
@sampler.expects(:disabled).returns(true)
|
|
643
|
+
@sampler.send(:start_builder)
|
|
644
|
+
assert_equal(nil, Thread.current[:transaction_sample_builder], "should clear the transaction builder when disabled")
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def test_start_builder_dont_replace_existing_builder
|
|
648
|
+
fake_builder = mock('transaction sample builder')
|
|
649
|
+
Thread.current[:transaction_sample_builder] = fake_builder
|
|
650
|
+
@sampler.expects(:disabled).returns(false)
|
|
651
|
+
@sampler.send(:start_builder)
|
|
652
|
+
assert_equal(fake_builder, Thread.current[:transaction_sample_builder], "should not overwrite an existing transaction sample builder")
|
|
653
|
+
Thread.current[:transaction_sample_builder] = nil
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def test_builder
|
|
657
|
+
Thread.current[:transaction_sample_builder] = 'shamalamadingdong, brother.'
|
|
658
|
+
assert_equal('shamalamadingdong, brother.', @sampler.send(:builder), 'should return the value from the thread local variable')
|
|
659
|
+
Thread.current[:transaction_sample_builder] = nil
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def test_clear_builder
|
|
663
|
+
Thread.current[:transaction_sample_builder] = 'shamalamadingdong, brother.'
|
|
664
|
+
assert_equal(nil, @sampler.send(:clear_builder), 'should clear the thread local variable')
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# Tests below this line are functional tests for the sampler, not
|
|
668
|
+
# unit tests per se - some overlap with the tests above, but
|
|
669
|
+
# generally usefully so
|
|
670
|
+
|
|
671
|
+
def test_multiple_samples
|
|
672
|
+
|
|
673
|
+
run_sample_trace
|
|
674
|
+
run_sample_trace
|
|
675
|
+
run_sample_trace
|
|
676
|
+
run_sample_trace
|
|
677
|
+
|
|
678
|
+
samples = @sampler.samples
|
|
679
|
+
assert_equal 4, samples.length
|
|
680
|
+
assert_equal "a", samples.first.root_segment.called_segments[0].metric_name
|
|
681
|
+
assert_equal "a", samples.last.root_segment.called_segments[0].metric_name
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
def test_sample_tree
|
|
685
|
+
assert_equal 0, @sampler.scope_depth
|
|
686
|
+
|
|
687
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
688
|
+
@sampler.notice_transaction "/path", nil, {}
|
|
689
|
+
@sampler.notice_push_scope "a"
|
|
690
|
+
|
|
691
|
+
@sampler.notice_push_scope "b"
|
|
692
|
+
@sampler.notice_pop_scope "b"
|
|
693
|
+
|
|
694
|
+
@sampler.notice_push_scope "c"
|
|
695
|
+
@sampler.notice_push_scope "d"
|
|
696
|
+
@sampler.notice_pop_scope "d"
|
|
697
|
+
@sampler.notice_pop_scope "c"
|
|
698
|
+
|
|
699
|
+
@sampler.notice_pop_scope "a"
|
|
700
|
+
@sampler.notice_scope_empty
|
|
701
|
+
sample = @sampler.harvest([],0.0).first
|
|
702
|
+
assert_equal "ROOT{a{b,c{d}}}", sample.to_s_compact
|
|
703
|
+
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def test_sample__gc_stats
|
|
707
|
+
GC.extend MockGCStats
|
|
708
|
+
# These are effectively Garbage Collects, detected each time GC.time is
|
|
709
|
+
# called by the transaction sampler. One time value in seconds for each call.
|
|
710
|
+
MockGCStats.mock_values = [0,0,0,1,0,0,1,0,0,0,0,0,0,0,0]
|
|
711
|
+
assert_equal 0, @sampler.scope_depth
|
|
712
|
+
|
|
713
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
714
|
+
@sampler.notice_transaction "/path", nil, {}
|
|
715
|
+
@sampler.notice_push_scope "a"
|
|
716
|
+
|
|
717
|
+
@sampler.notice_push_scope "b"
|
|
718
|
+
@sampler.notice_pop_scope "b"
|
|
719
|
+
|
|
720
|
+
@sampler.notice_push_scope "c"
|
|
721
|
+
@sampler.notice_push_scope "d"
|
|
722
|
+
@sampler.notice_pop_scope "d"
|
|
723
|
+
@sampler.notice_pop_scope "c"
|
|
724
|
+
|
|
725
|
+
@sampler.notice_pop_scope "a"
|
|
726
|
+
@sampler.notice_scope_empty
|
|
727
|
+
|
|
728
|
+
sample = @sampler.harvest([],0.0).first
|
|
729
|
+
assert_equal "ROOT{a{b,c{d}}}", sample.to_s_compact
|
|
730
|
+
ensure
|
|
731
|
+
MockGCStats.mock_values = []
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def test_sample_id
|
|
735
|
+
run_sample_trace do
|
|
736
|
+
assert((@sampler.current_sample_id && @sampler.current_sample_id != 0), @sampler.current_sample_id.to_s + ' should not be zero')
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
# NB this test occasionally fails due to a GC during one of the
|
|
742
|
+
# sample traces, for example. It's unfortunate, but we can't
|
|
743
|
+
# reliably turn off GC on all versions of ruby under test
|
|
744
|
+
def test_harvest_slowest
|
|
745
|
+
|
|
746
|
+
run_sample_trace
|
|
747
|
+
run_sample_trace
|
|
748
|
+
run_sample_trace { sleep 0.1 }
|
|
749
|
+
run_sample_trace
|
|
750
|
+
run_sample_trace
|
|
751
|
+
|
|
752
|
+
slowest = @sampler.harvest(nil, 0)[0]
|
|
753
|
+
assert((slowest.duration >= 0.09), "expected sample duration >= 0.09, but was: #{slowest.duration.inspect}")
|
|
754
|
+
# this assert is here to make sure the test remains valid
|
|
755
|
+
assert((slowest.duration <= 0.15), "expected sample duration <= 0.15, but was: #{slowest.duration.inspect}")
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
run_sample_trace { sleep 0.0001 }
|
|
759
|
+
not_as_slow = @sampler.harvest(slowest, 0)[0]
|
|
760
|
+
assert((not_as_slow == slowest), "Should re-harvest the same transaction since it should be slower than the new transaction - expected #{slowest.inspect} but got #{not_as_slow.inspect}")
|
|
761
|
+
|
|
762
|
+
run_sample_trace { sleep 0.16 }
|
|
763
|
+
new_slowest = @sampler.harvest(slowest, 0)[0]
|
|
764
|
+
assert((new_slowest != slowest), "Should not harvest the same trace since the new one should be slower")
|
|
765
|
+
assert((new_slowest.duration >= 0.15), "Slowest duration must be >= 0.15, but was: #{new_slowest.duration.inspect}")
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def test_prepare_to_send
|
|
770
|
+
|
|
771
|
+
run_sample_trace { sleep 0.002 }
|
|
772
|
+
sample = @sampler.harvest(nil, 0)[0]
|
|
773
|
+
|
|
774
|
+
ready_to_send = sample.prepare_to_send
|
|
775
|
+
assert sample.duration == ready_to_send.duration
|
|
776
|
+
|
|
777
|
+
assert ready_to_send.start_time.is_a?(Time)
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def test_multithread
|
|
781
|
+
threads = []
|
|
782
|
+
|
|
783
|
+
5.times do
|
|
784
|
+
t = Thread.new(@sampler) do |the_sampler|
|
|
785
|
+
@sampler = the_sampler
|
|
786
|
+
10.times do
|
|
787
|
+
run_sample_trace { sleep 0.0001 }
|
|
788
|
+
end
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
threads << t
|
|
792
|
+
end
|
|
793
|
+
threads.each {|t| t.join }
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
def test_sample_with_parallel_paths
|
|
797
|
+
|
|
798
|
+
assert_equal 0, @sampler.scope_depth
|
|
799
|
+
|
|
800
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
801
|
+
@sampler.notice_transaction "/path", nil, {}
|
|
802
|
+
@sampler.notice_push_scope "a"
|
|
803
|
+
|
|
804
|
+
assert_equal 1, @sampler.scope_depth
|
|
805
|
+
|
|
806
|
+
@sampler.notice_pop_scope "a"
|
|
807
|
+
@sampler.notice_scope_empty
|
|
808
|
+
|
|
809
|
+
assert_equal 0, @sampler.scope_depth
|
|
810
|
+
|
|
811
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
812
|
+
@sampler.notice_transaction "/path", nil, {}
|
|
813
|
+
@sampler.notice_push_scope "a"
|
|
814
|
+
@sampler.notice_pop_scope "a"
|
|
815
|
+
@sampler.notice_scope_empty
|
|
816
|
+
|
|
817
|
+
assert_equal 0, @sampler.scope_depth
|
|
818
|
+
sample = @sampler.harvest(nil, 0.0).first
|
|
819
|
+
assert_equal "ROOT{a}", sample.to_s_compact
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def test_double_scope_stack_empty
|
|
823
|
+
|
|
824
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
825
|
+
@sampler.notice_transaction "/path", nil, {}
|
|
826
|
+
@sampler.notice_push_scope "a"
|
|
827
|
+
@sampler.notice_pop_scope "a"
|
|
828
|
+
@sampler.notice_scope_empty
|
|
829
|
+
@sampler.notice_scope_empty
|
|
830
|
+
@sampler.notice_scope_empty
|
|
831
|
+
@sampler.notice_scope_empty
|
|
832
|
+
|
|
833
|
+
assert_not_nil @sampler.harvest(nil, 0)[0]
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def test_record_sql_off
|
|
838
|
+
|
|
839
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
840
|
+
|
|
841
|
+
Thread::current[:record_sql] = false
|
|
842
|
+
|
|
843
|
+
@sampler.notice_sql("test", nil, 0)
|
|
844
|
+
|
|
845
|
+
segment = @sampler.send(:builder).current_segment
|
|
846
|
+
|
|
847
|
+
assert_nil segment[:sql]
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
def test_stack_trace__sql
|
|
851
|
+
@sampler.stack_trace_threshold = 0
|
|
852
|
+
|
|
853
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
854
|
+
|
|
855
|
+
@sampler.notice_sql("test", nil, 1)
|
|
856
|
+
|
|
857
|
+
segment = @sampler.send(:builder).current_segment
|
|
858
|
+
|
|
859
|
+
assert segment[:sql]
|
|
860
|
+
assert segment[:backtrace]
|
|
861
|
+
end
|
|
862
|
+
def test_stack_trace__scope
|
|
863
|
+
|
|
864
|
+
@sampler.stack_trace_threshold = 0
|
|
865
|
+
t = Time.now
|
|
866
|
+
@sampler.notice_first_scope_push t.to_f
|
|
867
|
+
@sampler.notice_push_scope 'Bill', (t+1).to_f
|
|
868
|
+
|
|
869
|
+
segment = @sampler.send(:builder).current_segment
|
|
870
|
+
assert segment[:backtrace]
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def test_nil_stacktrace
|
|
874
|
+
|
|
875
|
+
@sampler.stack_trace_threshold = 2
|
|
876
|
+
|
|
877
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
878
|
+
|
|
879
|
+
@sampler.notice_sql("test", nil, 1)
|
|
880
|
+
|
|
881
|
+
segment = @sampler.send(:builder).current_segment
|
|
882
|
+
|
|
883
|
+
assert segment[:sql]
|
|
884
|
+
assert_nil segment[:backtrace]
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
def test_big_sql
|
|
888
|
+
|
|
889
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
890
|
+
|
|
891
|
+
sql = "SADJKHASDHASD KAJSDH ASKDH ASKDHASDK JASHD KASJDH ASKDJHSAKDJHAS DKJHSADKJSAH DKJASHD SAKJDH SAKDJHS"
|
|
892
|
+
|
|
893
|
+
len = 0
|
|
894
|
+
while len <= 16384
|
|
895
|
+
@sampler.notice_sql(sql, nil, 0)
|
|
896
|
+
len += sql.length
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
segment = @sampler.send(:builder).current_segment
|
|
900
|
+
|
|
901
|
+
sql = segment[:sql]
|
|
902
|
+
|
|
903
|
+
assert sql.length <= 16384
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def test_segment_obfuscated
|
|
908
|
+
|
|
909
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
910
|
+
@sampler.notice_push_scope "foo"
|
|
911
|
+
|
|
912
|
+
orig_sql = "SELECT * from Jim where id=66"
|
|
913
|
+
|
|
914
|
+
@sampler.notice_sql(orig_sql, nil, 0)
|
|
915
|
+
|
|
916
|
+
segment = @sampler.send(:builder).current_segment
|
|
917
|
+
|
|
918
|
+
assert_equal orig_sql, segment[:sql]
|
|
919
|
+
assert_equal "SELECT * from Jim where id=?", segment.obfuscated_sql
|
|
920
|
+
@sampler.notice_pop_scope "foo"
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def test_param_capture
|
|
925
|
+
[true, false].each do |capture|
|
|
926
|
+
NewRelic::Control.instance.stubs(:capture_params).returns(capture)
|
|
927
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
928
|
+
@sampler.notice_transaction('/path', nil, {:param => 'hi'})
|
|
929
|
+
@sampler.notice_scope_empty
|
|
930
|
+
|
|
931
|
+
tt = @sampler.harvest(nil,0)[0]
|
|
932
|
+
|
|
933
|
+
assert_equal (capture) ? 1 : 0, tt.params[:request_params].length
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
private
|
|
939
|
+
def run_sample_trace(&proc)
|
|
940
|
+
@sampler.notice_first_scope_push Time.now.to_f
|
|
941
|
+
@sampler.notice_transaction '/path', nil, {}
|
|
942
|
+
@sampler.notice_push_scope "a"
|
|
943
|
+
@sampler.notice_sql("SELECT * FROM sandwiches WHERE bread = 'wheat'", nil, 0)
|
|
944
|
+
@sampler.notice_push_scope "ab"
|
|
945
|
+
@sampler.notice_sql("SELECT * FROM sandwiches WHERE bread = 'white'", nil, 0)
|
|
946
|
+
proc.call if proc
|
|
947
|
+
@sampler.notice_pop_scope "ab"
|
|
948
|
+
@sampler.notice_push_scope "lew"
|
|
949
|
+
@sampler.notice_sql("SELECT * FROM sandwiches WHERE bread = 'french'", nil, 0)
|
|
950
|
+
@sampler.notice_pop_scope "lew"
|
|
951
|
+
@sampler.notice_pop_scope "a"
|
|
952
|
+
@sampler.notice_scope_empty
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
end
|