bond-spy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ]