bond-spy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +27 -0
  7. data/README.rst +65 -0
  8. data/Rakefile +6 -0
  9. data/bin/bond_reconcile.py +385 -0
  10. data/bin/setup +5 -0
  11. data/bond.gemspec +36 -0
  12. data/lib/bond.rb +469 -0
  13. data/lib/bond/spec_helper.rb +32 -0
  14. data/lib/bond/targetable.rb +210 -0
  15. data/lib/bond/version.rb +3 -0
  16. data/spec/bond_spec.rb +158 -0
  17. data/spec/bond_targetable_spec.rb +202 -0
  18. data/spec/spec_helper.rb +2 -0
  19. data/spec/test_observations/.gitignore +2 -0
  20. data/spec/test_observations/bond_spec/Bond_with_agents_should_call_doers_before_returning_result.json +14 -0
  21. data/spec/test_observations/bond_spec/Bond_with_agents_should_call_the_function_passed_as_result_if_it_is_callable.json +23 -0
  22. data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_a_single_doer_if_filter_criteria_are_met.json +10 -0
  23. data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_multiple_doers.json +13 -0
  24. data/spec/test_observations/bond_spec/Bond_with_agents_should_not_call_doers_of_overriden_agents.json +8 -0
  25. data/spec/test_observations/bond_spec/Bond_with_agents_should_override_old_agents_with_newer_agents.json +0 -0
  26. data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_an_exception_if_specified_by_agent.json +9 -0
  27. data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_the_result_of_the_value_passed_to_exception_if_callable.json +10 -0
  28. data/spec/test_observations/bond_spec/Bond_with_agents_should_work_with_multiple_agents_for_different_spy_points.json +23 -0
  29. data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_override_old_agents_with_newer_agents_unless_theott8glo1xn.json +22 -0
  30. data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_combinations_of_filters.json +37 -0
  31. data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_function_filters.json +16 -0
  32. data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_single_key_value_filters_of_all_types.json +121 -0
  33. data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_nested_hashes_and_arrays_with_hash_sorting.json +31 -0
  34. data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_with_a_spy_point_name.json +12 -0
  35. data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_without_a_spy_point_name.json +10 -0
  36. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_continue_is_returned.json +10 -0
  37. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_none_is_returned.json +14 -0
  38. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_passes_through_blocks.json +10 -0
  39. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_returns_nil__and_mocks__when_an_agent_returns_nil.json +11 -0
  40. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_private_methods.json +6 -0
  41. data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_protected_methods.json +6 -0
  42. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_class_method.json +7 -0
  43. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_method_with_variabl19nhijeqoo.json +13 -0
  44. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_positional_a6qc3d4el92.json +33 -0
  45. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_positional_aott8glo1xn.json +20 -0
  46. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_required_andbcgjq06had.json +17 -0
  47. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_normal_method.json +7 -0
  48. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_all_optional_keyword_arguments.json +10 -0
  49. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_variable_keyword_arguments.json +22 -0
  50. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_changes_the_spy_point_naj4gnwvcu8n.json +5 -0
  51. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_errors_when_mocking_is_r9j7wklng0z.json +6 -0
  52. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_ignores_excluded_keys.json +10 -0
  53. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_mocks_when_one_is_specified.json +10 -0
  54. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_spies_the_return_value_w19nhijeqoo.json +10 -0
  55. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_spies_the_return_value_ww8esw1qdxc.json +10 -0
  56. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_included_module_methods.json +6 -0
  57. data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_module_methods.json +7 -0
  58. data/tutorials/binary_search_tree/bst.rb +120 -0
  59. data/tutorials/binary_search_tree/bst_spec.rb +82 -0
  60. data/tutorials/binary_search_tree/run_tests.sh +4 -0
  61. data/tutorials/binary_search_tree/test_observations/.gitignore +2 -0
  62. data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_add_nodes_to_the_BST_correctly__testing_with_Bond.json +20 -0
  63. data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_add_nodes_to_the_BST_correctly__testing_without_Bond.json +3 -0
  64. data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_correctly_delete_nodes_from_the_BST.json +29 -0
  65. data/tutorials/heat_watcher/heat_watcher.rb +107 -0
  66. data/tutorials/heat_watcher/heat_watcher_spec.rb +116 -0
  67. data/tutorials/heat_watcher/run_tests.sh +4 -0
  68. data/tutorials/heat_watcher/test_observations/.gitignore +2 -0
  69. data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_critical_errors.json +142 -0
  70. data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_warnings_and_switch_back_to_OK_status.json +132 -0
  71. metadata +211 -0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ this_dir=`dirname $0`
3
+ this_dir=`cd $this_dir && pwd`
4
+ BOND_MERGE=console RUBYLIB="$this_dir/../../lib" rspec bst_spec.rb
@@ -0,0 +1,2 @@
1
+ *_now.json
2
+ *.diff
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "__spy_point__": "test_add_1",
4
+ "tree": {
5
+ "data": 8,
6
+ "left": {
7
+ "data": 3,
8
+ "right": {
9
+ "data": 4,
10
+ "right": {
11
+ "data": 6
12
+ }
13
+ }
14
+ },
15
+ "right": {
16
+ "data": 12
17
+ }
18
+ }
19
+ }
20
+ ]
@@ -0,0 +1,29 @@
1
+ [
2
+ {
3
+ "__spy_point__": "test_delete_1",
4
+ "tree": {
5
+ "data": 8,
6
+ "left": {
7
+ "data": 3,
8
+ "right": {
9
+ "data": 6
10
+ }
11
+ },
12
+ "right": {
13
+ "data": 12
14
+ }
15
+ }
16
+ },
17
+ {
18
+ "__spy_point__": "test_delete_2",
19
+ "tree": {
20
+ "data": 12,
21
+ "left": {
22
+ "data": 3,
23
+ "right": {
24
+ "data": 6
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ]
@@ -0,0 +1,107 @@
1
+ #
2
+ # A simple demonstration of using Bond for spying and mocking
3
+ # an application for monitoring temperature and sending alerts
4
+ #
5
+ # See a full explanation of this example at
6
+ # http://necula01.github.io/bond/example_heat.html
7
+ #
8
+ # rst_Start
9
+
10
+ require 'bond'
11
+
12
+ # Monitor temperature rise over time.
13
+ # See description in the Bond documentation.
14
+ class HeatWatcher
15
+ # Marks this class as being able to be spied on. Exports `bond` method
16
+ # to use as e.g. bond.spy and bond.spy_point.
17
+ extend BondTargetable
18
+
19
+ def initialize
20
+ @last_temp = nil # The last temp measurement
21
+ @last_time = nil # The time when we took the last measurement
22
+ @last_alert_state = 'Ok' # Ok, Warning, Critical
23
+ @last_alert_time = -Float::INFINITY # The time when we sent the last alert
24
+ end
25
+
26
+ # Monitor the temperature and send alerts
27
+ # @param exit_time the time when to exit the monitor loop.
28
+ def monitor_loop(exit_time = nil)
29
+ loop do
30
+ temp = get_temperature
31
+ now = get_current_time
32
+ return if !exit_time.nil? && now >= exit_time
33
+
34
+ if @last_temp.nil?
35
+ # First reading
36
+ @last_temp = temp
37
+ @last_time = now
38
+ interval = 60
39
+ else
40
+ change_rate = (temp - @last_temp) / (now - @last_time) * 60
41
+ if change_rate < 1
42
+ interval = 60
43
+ alert_state = 'Ok'
44
+ elsif change_rate < 2
45
+ interval = 10
46
+ alert_state = 'Warning'
47
+ else
48
+ interval = 10
49
+ alert_state = 'Critical'
50
+ end
51
+
52
+ @last_temp = temp
53
+ @last_time = now
54
+
55
+ if alert_state != @last_alert_state ||
56
+ (alert_state != 'Ok' && now >= 600 + @last_alert_time)
57
+ # Send an alert
58
+ send_alert("#{alert_state}: Temperature is rising at #{change_rate.round(1)} deg/min")
59
+ @last_alert_time = now
60
+ end
61
+
62
+ @last_alert_state = alert_state
63
+ end
64
+
65
+ sleep(interval)
66
+ end
67
+ end
68
+
69
+ # Spy this function, want to spy the result
70
+ bond.spy_point(spy_result: true)
71
+ # Read the temperature from a sensor
72
+ def get_temperature
73
+ resp_code, temp_data =
74
+ make_request('http://system.server.com/temperature')
75
+ raise 'Error while retrieving temperature!' unless resp_code == 200
76
+ match = /<temperature>([0-9.]+)<\/temperature>/.match(temp_data)
77
+ raise "Error while parsing temperature from: #{temp_data}" if match.nil?
78
+ match[1].to_f
79
+ end
80
+
81
+ bond.spy_point
82
+ # Read the current time
83
+ def get_current_time
84
+ Time.now.to_i
85
+ end
86
+
87
+
88
+ bond.spy_point
89
+ # Sleep a few seconds
90
+ def sleep(seconds)
91
+ sleep(seconds)
92
+ end
93
+
94
+ bond.spy_point
95
+ # Send an alert
96
+ def send_alert(message)
97
+ make_request('http://backend.server.com/messages', {message: message})
98
+ end
99
+
100
+ bond.spy_point(require_agent_result: true)
101
+ # HTTP request (GET, or POST if the data is provided)
102
+ def make_request(url, data = nil)
103
+ full_url = "#{url}?#{URI.encode_www_form(data)}"
104
+ resp = Net::HTTP.get_response(URI(full_url))
105
+ [resp.code.to_i, resp.body]
106
+ end
107
+ end
@@ -0,0 +1,116 @@
1
+ #
2
+ # Tests using Bond for the heat_watcher.py app
3
+ #
4
+ # See a full explanation of this example at
5
+ # http://necula01.github.io/bond/example_heat.html
6
+ #
7
+
8
+ require 'bond/spec_helper'
9
+ require_relative 'heat_watcher'
10
+
11
+ describe HeatWatcher do
12
+ # Sets up the testing environment to use Bond. Exports bond as a variable
13
+ # to use as e.g. bond.deploy_agent.
14
+ include_context :bond
15
+
16
+ # Helper function to setup the time mocks and agents
17
+ def deploy_time_mock
18
+ @time_mocker = TimeMocker.new
19
+
20
+ bond.deploy_agent('HeatWatcher#get_current_time',
21
+ result: lambda { |_| @time_mocker.time })
22
+ bond.deploy_agent('HeatWatcher#sleep',
23
+ result: lambda { |obs| @time_mocker.sleep(obs[:seconds]) })
24
+ end
25
+
26
+ it 'should properly report warnings and switch back to OK status' do
27
+ # A test where the higher-level functions get_temperature and send_alert
28
+ # are mocked out to return specified temperatures and do nothing, respectively.
29
+ deploy_time_mock
30
+ @temp_mocker = TemperatureMocker.new(time_mocker: @time_mocker,
31
+ temp_start: 70,
32
+ temp_rates: [[0, 0.5], [60, 1.3], [110, 0.1]])
33
+
34
+ bond.deploy_agent('HeatWatcher#get_temperature',
35
+ result: lambda { |_| @temp_mocker.temperature })
36
+ bond.deploy_agent('HeatWatcher#send_alert',
37
+ result: nil)
38
+
39
+ HeatWatcher.new.monitor_loop(@time_mocker.time + 400)
40
+ end
41
+
42
+ it 'should properly report critical errors' do
43
+ # A test where make_request is mocked out, and returns different responses
44
+ # depending on the URL that is passed in. This can allow you to test that the
45
+ # parsing logic in get_temperature is working correctly.
46
+ deploy_time_mock
47
+ @temp_mocker = TemperatureMocker.new(time_mocker: @time_mocker,
48
+ temp_start: 70,
49
+ temp_rates: [[0, 0.5], [60, 2.5], [120, 3], [140, 0.5]])
50
+
51
+ bond.deploy_agent('HeatWatcher#make_request',
52
+ url__contains: 'messages',
53
+ result: nil)
54
+ bond.deploy_agent('HeatWatcher#make_request',
55
+ url__contains: 'temperature',
56
+ result: lambda do |_|
57
+ [200, "<temperature>#{@temp_mocker.temperature}</temperature>"]
58
+ end)
59
+
60
+ HeatWatcher.new.monitor_loop(@time_mocker.time + 210)
61
+ end
62
+ end
63
+
64
+ # rst_TimeMocker
65
+ # A class to mock time
66
+ class TimeMocker
67
+ def initialize(current_time = 1445567700)
68
+ # Default 10/23/2015 @ 2:35am (UTC)
69
+ @current_time = current_time
70
+ end
71
+
72
+ def time
73
+ @current_time
74
+ end
75
+
76
+ def sleep(seconds)
77
+ @current_time += seconds
78
+ end
79
+ end
80
+
81
+ # rst_TemperatureMocker
82
+ # A class to mock temperature
83
+ class TemperatureMocker
84
+
85
+ # @param time_mocker a reference to the current time mocker
86
+ # @param temp_start the starting temperature
87
+ # @param temp_rates a list of pairs (time_since_start,
88
+ # temperature_increase_rate_per_min) ordered by time_since_start
89
+ def initialize(time_mocker:, temp_start:, temp_rates: [])
90
+ @time_mocker = time_mocker
91
+ @start_time = time_mocker.time
92
+ @last_temp = temp_start # last temp read
93
+ @last_temp_time = @start_time # last temp read time
94
+ @temp_rates = temp_rates
95
+ end
96
+
97
+ def temperature
98
+ now = @time_mocker.time
99
+ time_since_start = now - @start_time
100
+ # See if we need to advance to the next temperature rate
101
+ if @temp_rates.length > 1 && time_since_start >= @temp_rates[1][0]
102
+
103
+ old_rate = @temp_rates.shift[1]
104
+
105
+ old_rate_time = @temp_rates[0][0]
106
+ @last_temp += (old_rate_time + @start_time - @last_temp_time) / 60.0 * old_rate
107
+ @last_temp_time = old_rate_time + @start_time
108
+ end
109
+
110
+ # The first pair is the one we use to get the rate
111
+ rate = @temp_rates.length > 0 ? @temp_rates[0][1] : 0
112
+ @last_temp += (now - @last_temp_time) / 60.0 * rate
113
+ @last_temp_time = now
114
+ @last_temp
115
+ end
116
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+ this_dir=`dirname $0`
3
+ this_dir=`cd $this_dir && pwd`
4
+ BOND_MERGE=console RUBYLIB="$this_dir/../../lib" rspec heat_watcher_spec.rb
@@ -0,0 +1,2 @@
1
+ *_now.json
2
+ *.diff
@@ -0,0 +1,142 @@
1
+ [
2
+ {
3
+ "__spy_point__": "HeatWatcher#get_temperature"
4
+ },
5
+ {
6
+ "__spy_point__": "HeatWatcher#make_request",
7
+ "url": "http://system.server.com/temperature"
8
+ },
9
+ {
10
+ "__spy_point__": "HeatWatcher#get_temperature.result",
11
+ "result": 70.0
12
+ },
13
+ {
14
+ "__spy_point__": "HeatWatcher#get_current_time"
15
+ },
16
+ {
17
+ "__spy_point__": "HeatWatcher#sleep",
18
+ "seconds": 60
19
+ },
20
+ {
21
+ "__spy_point__": "HeatWatcher#get_temperature"
22
+ },
23
+ {
24
+ "__spy_point__": "HeatWatcher#make_request",
25
+ "url": "http://system.server.com/temperature"
26
+ },
27
+ {
28
+ "__spy_point__": "HeatWatcher#get_temperature.result",
29
+ "result": 70.5
30
+ },
31
+ {
32
+ "__spy_point__": "HeatWatcher#get_current_time"
33
+ },
34
+ {
35
+ "__spy_point__": "HeatWatcher#sleep",
36
+ "seconds": 60
37
+ },
38
+ {
39
+ "__spy_point__": "HeatWatcher#get_temperature"
40
+ },
41
+ {
42
+ "__spy_point__": "HeatWatcher#make_request",
43
+ "url": "http://system.server.com/temperature"
44
+ },
45
+ {
46
+ "__spy_point__": "HeatWatcher#get_temperature.result",
47
+ "result": 73.0
48
+ },
49
+ {
50
+ "__spy_point__": "HeatWatcher#get_current_time"
51
+ },
52
+ {
53
+ "__spy_point__": "HeatWatcher#send_alert",
54
+ "message": "Critical: Temperature is rising at 2.5 deg/min"
55
+ },
56
+ {
57
+ "__spy_point__": "HeatWatcher#make_request",
58
+ "message": "Critical: Temperature is rising at 2.5 deg/min",
59
+ "url": "http://backend.server.com/messages"
60
+ },
61
+ {
62
+ "__spy_point__": "HeatWatcher#sleep",
63
+ "seconds": 10
64
+ },
65
+ {
66
+ "__spy_point__": "HeatWatcher#get_temperature"
67
+ },
68
+ {
69
+ "__spy_point__": "HeatWatcher#make_request",
70
+ "url": "http://system.server.com/temperature"
71
+ },
72
+ {
73
+ "__spy_point__": "HeatWatcher#get_temperature.result",
74
+ "result": 73.5
75
+ },
76
+ {
77
+ "__spy_point__": "HeatWatcher#get_current_time"
78
+ },
79
+ {
80
+ "__spy_point__": "HeatWatcher#sleep",
81
+ "seconds": 10
82
+ },
83
+ {
84
+ "__spy_point__": "HeatWatcher#get_temperature"
85
+ },
86
+ {
87
+ "__spy_point__": "HeatWatcher#make_request",
88
+ "url": "http://system.server.com/temperature"
89
+ },
90
+ {
91
+ "__spy_point__": "HeatWatcher#get_temperature.result",
92
+ "result": 74.0
93
+ },
94
+ {
95
+ "__spy_point__": "HeatWatcher#get_current_time"
96
+ },
97
+ {
98
+ "__spy_point__": "HeatWatcher#sleep",
99
+ "seconds": 10
100
+ },
101
+ {
102
+ "__spy_point__": "HeatWatcher#get_temperature"
103
+ },
104
+ {
105
+ "__spy_point__": "HeatWatcher#make_request",
106
+ "url": "http://system.server.com/temperature"
107
+ },
108
+ {
109
+ "__spy_point__": "HeatWatcher#get_temperature.result",
110
+ "result": 74.08333333333333
111
+ },
112
+ {
113
+ "__spy_point__": "HeatWatcher#get_current_time"
114
+ },
115
+ {
116
+ "__spy_point__": "HeatWatcher#send_alert",
117
+ "message": "Ok: Temperature is rising at 0.5 deg/min"
118
+ },
119
+ {
120
+ "__spy_point__": "HeatWatcher#make_request",
121
+ "message": "Ok: Temperature is rising at 0.5 deg/min",
122
+ "url": "http://backend.server.com/messages"
123
+ },
124
+ {
125
+ "__spy_point__": "HeatWatcher#sleep",
126
+ "seconds": 60
127
+ },
128
+ {
129
+ "__spy_point__": "HeatWatcher#get_temperature"
130
+ },
131
+ {
132
+ "__spy_point__": "HeatWatcher#make_request",
133
+ "url": "http://system.server.com/temperature"
134
+ },
135
+ {
136
+ "__spy_point__": "HeatWatcher#get_temperature.result",
137
+ "result": 74.58333333333333
138
+ },
139
+ {
140
+ "__spy_point__": "HeatWatcher#get_current_time"
141
+ }
142
+ ]
@@ -0,0 +1,132 @@
1
+ [
2
+ {
3
+ "__spy_point__": "HeatWatcher#get_temperature"
4
+ },
5
+ {
6
+ "__spy_point__": "HeatWatcher#get_temperature.result",
7
+ "result": 70.0
8
+ },
9
+ {
10
+ "__spy_point__": "HeatWatcher#get_current_time"
11
+ },
12
+ {
13
+ "__spy_point__": "HeatWatcher#sleep",
14
+ "seconds": 60
15
+ },
16
+ {
17
+ "__spy_point__": "HeatWatcher#get_temperature"
18
+ },
19
+ {
20
+ "__spy_point__": "HeatWatcher#get_temperature.result",
21
+ "result": 70.5
22
+ },
23
+ {
24
+ "__spy_point__": "HeatWatcher#get_current_time"
25
+ },
26
+ {
27
+ "__spy_point__": "HeatWatcher#sleep",
28
+ "seconds": 60
29
+ },
30
+ {
31
+ "__spy_point__": "HeatWatcher#get_temperature"
32
+ },
33
+ {
34
+ "__spy_point__": "HeatWatcher#get_temperature.result",
35
+ "result": 71.6
36
+ },
37
+ {
38
+ "__spy_point__": "HeatWatcher#get_current_time"
39
+ },
40
+ {
41
+ "__spy_point__": "HeatWatcher#send_alert",
42
+ "message": "Warning: Temperature is rising at 1.1 deg/min"
43
+ },
44
+ {
45
+ "__spy_point__": "HeatWatcher#sleep",
46
+ "seconds": 10
47
+ },
48
+ {
49
+ "__spy_point__": "HeatWatcher#get_temperature"
50
+ },
51
+ {
52
+ "__spy_point__": "HeatWatcher#get_temperature.result",
53
+ "result": 71.61666666666666
54
+ },
55
+ {
56
+ "__spy_point__": "HeatWatcher#get_current_time"
57
+ },
58
+ {
59
+ "__spy_point__": "HeatWatcher#send_alert",
60
+ "message": "Ok: Temperature is rising at 0.1 deg/min"
61
+ },
62
+ {
63
+ "__spy_point__": "HeatWatcher#sleep",
64
+ "seconds": 60
65
+ },
66
+ {
67
+ "__spy_point__": "HeatWatcher#get_temperature"
68
+ },
69
+ {
70
+ "__spy_point__": "HeatWatcher#get_temperature.result",
71
+ "result": 71.71666666666665
72
+ },
73
+ {
74
+ "__spy_point__": "HeatWatcher#get_current_time"
75
+ },
76
+ {
77
+ "__spy_point__": "HeatWatcher#sleep",
78
+ "seconds": 60
79
+ },
80
+ {
81
+ "__spy_point__": "HeatWatcher#get_temperature"
82
+ },
83
+ {
84
+ "__spy_point__": "HeatWatcher#get_temperature.result",
85
+ "result": 71.81666666666665
86
+ },
87
+ {
88
+ "__spy_point__": "HeatWatcher#get_current_time"
89
+ },
90
+ {
91
+ "__spy_point__": "HeatWatcher#sleep",
92
+ "seconds": 60
93
+ },
94
+ {
95
+ "__spy_point__": "HeatWatcher#get_temperature"
96
+ },
97
+ {
98
+ "__spy_point__": "HeatWatcher#get_temperature.result",
99
+ "result": 71.91666666666664
100
+ },
101
+ {
102
+ "__spy_point__": "HeatWatcher#get_current_time"
103
+ },
104
+ {
105
+ "__spy_point__": "HeatWatcher#sleep",
106
+ "seconds": 60
107
+ },
108
+ {
109
+ "__spy_point__": "HeatWatcher#get_temperature"
110
+ },
111
+ {
112
+ "__spy_point__": "HeatWatcher#get_temperature.result",
113
+ "result": 72.01666666666664
114
+ },
115
+ {
116
+ "__spy_point__": "HeatWatcher#get_current_time"
117
+ },
118
+ {
119
+ "__spy_point__": "HeatWatcher#sleep",
120
+ "seconds": 60
121
+ },
122
+ {
123
+ "__spy_point__": "HeatWatcher#get_temperature"
124
+ },
125
+ {
126
+ "__spy_point__": "HeatWatcher#get_temperature.result",
127
+ "result": 72.11666666666663
128
+ },
129
+ {
130
+ "__spy_point__": "HeatWatcher#get_current_time"
131
+ }
132
+ ]