bond-spy 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/bond_reconcile.py +224 -134
- data/bond.gemspec +2 -1
- data/lib/bond.rb +53 -35
- data/lib/bond/targetable.rb +9 -3
- data/lib/bond/version.rb +1 -1
- data/spec/bond_spec.rb +16 -0
- data/spec/bond_targetable_spec.rb +13 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_call_doers_before_returning_result.json +4 -4
- data/spec/test_observations/bond_spec/Bond_with_agents_should_call_the_function_passed_as_result_if_it_is_callable.json +9 -9
- data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_a_single_doer_if_filter_criteria_are_met.json +4 -4
- data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_multiple_doers.json +5 -5
- data/spec/test_observations/bond_spec/Bond_with_agents_should_not_call_doers_of_overriden_agents.json +2 -2
- data/spec/test_observations/bond_spec/Bond_with_agents_should_skip_saving_observations_when_specified.json +18 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_an_exception_if_specified_by_agent.json +3 -3
- data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_the_result_of_the_value_passed_to_exception_if_callable.json +4 -4
- data/spec/test_observations/bond_spec/Bond_with_agents_should_work_with_multiple_agents_for_different_spy_points.json +9 -9
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_combinations_of_filters.json +19 -19
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_function_filters.json +6 -6
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_single_key_value_filters_of_all_types.json +51 -51
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_nested_hashes_and_arrays_with_hash_sorting.json +22 -22
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_with_a_spy_point_name.json +6 -6
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_without_a_spy_point_name.json +4 -4
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_continue_is_returned.json +4 -4
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_none_is_returned.json +6 -6
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_private_methods.json +2 -2
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_protected_methods.json +2 -2
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_class_method.json +3 -3
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_normal_method.json +3 -3
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_all_optional_keyword_arguments.json +4 -4
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_variable_keyword_arguments.json +12 -12
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_ignores_excluded_keys.json +4 -4
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_mocks_when_one_is_specified.json +4 -4
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_respects_mock_only.json +17 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_included_module_methods.json +2 -2
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_module_methods.json +3 -3
- data/tutorials/binary_search_tree/bst_spec.rb +1 -5
- data/tutorials/binary_search_tree/run_tests.sh +1 -1
- data/tutorials/heat_watcher/heat_watcher.rb +2 -2
- data/tutorials/heat_watcher/heat_watcher_spec.rb +8 -13
- data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_critical_errors.json +7 -49
- data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_warnings_and_switch_back_to_OK_status.json +9 -63
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c65fb7c7aa4cf11b582ae1620a91b522d88aaee3
|
4
|
+
data.tar.gz: fc50ff99e60d3c387394c886d48196722f056c6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfaa6acd7d9c78967231e194b652db630b271d07d6091a0bc7add63b8c598bf253ca97bd9451216d98203e5a896cd51a8789b999b1ceefd2a73928b7f03aba75
|
7
|
+
data.tar.gz: 72e3e53c5616f4948f8a0d0c2bbf0d758fa2df145a16161bf905421143bc49bebd1946c9596fbc4daf14a52b82f20d8edf3416e1018b6320263ea764ed1e6efe
|
data/bin/bond_reconcile.py
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
from __future__ import print_function
|
4
4
|
|
5
5
|
import os
|
6
|
-
import
|
7
|
-
import
|
6
|
+
import difflib
|
7
|
+
import string
|
8
|
+
import random
|
8
9
|
import sys
|
10
|
+
from bond_dialog import OptionDialog
|
9
11
|
|
10
12
|
try:
|
11
13
|
# Import bond safely
|
@@ -24,6 +26,8 @@ class ReconcileTool:
|
|
24
26
|
Base class for reconcile tools
|
25
27
|
"""
|
26
28
|
|
29
|
+
TMP_FILE_BASE_NAME = '/tmp/bond_tmp_'
|
30
|
+
|
27
31
|
@staticmethod
|
28
32
|
def select(reconcile_tool=None):
|
29
33
|
|
@@ -36,12 +40,15 @@ class ReconcileTool:
|
|
36
40
|
if reconcile_tool == 'console':
|
37
41
|
return ReconcileToolConsole()
|
38
42
|
|
43
|
+
if reconcile_tool == 'dialog':
|
44
|
+
return ReconcileToolDialog()
|
45
|
+
|
39
46
|
if reconcile_tool == 'kdiff3':
|
40
47
|
return ReconcileToolKdiff3()
|
41
48
|
|
42
49
|
if reconcile_tool is None:
|
43
50
|
# Look at the environment variable BOND_RECONCILE
|
44
|
-
reconcile_tool = os.environ.get('BOND_RECONCILE', '
|
51
|
+
reconcile_tool = os.environ.get('BOND_RECONCILE', 'console')
|
45
52
|
if reconcile_tool is not None:
|
46
53
|
return ReconcileTool.select(reconcile_tool)
|
47
54
|
|
@@ -59,16 +66,69 @@ class ReconcileTool:
|
|
59
66
|
"""
|
60
67
|
return os.system(cmd)
|
61
68
|
|
69
|
+
@staticmethod
|
70
|
+
def _get_user_input_console(prompt, options, single_char_options):
|
71
|
+
"""
|
72
|
+
Get input from the user using a console.
|
73
|
+
:param prompt: The main prompt string to display to the user.
|
74
|
+
:param options: A tuple of options to present to the user for them to select; must have
|
75
|
+
at least one. The last option is the default.
|
76
|
+
:param single_char_options: A tuple of the single-character versions of each of the
|
77
|
+
options supplied; this tuple must be the same length as ``options``,
|
78
|
+
and each single-character version must appear within the option itself.
|
79
|
+
:return: The user response, which is guaranteed to be one of the supplied ``options`` (if the user
|
80
|
+
input an invalid response the default option is used).
|
81
|
+
"""
|
82
|
+
opts_with_single_char = \
|
83
|
+
[string.replace(opt, char, '[' + char + ']', 1) for opt, char in zip(options, single_char_options)]
|
84
|
+
# Default option, highlight it in bold
|
85
|
+
opts_with_single_char[-1] = '\033[1m' + opts_with_single_char[-1] + '\033[0m'
|
86
|
+
response = raw_input(prompt + ' (' + ' | '.join(opts_with_single_char) + '): ')
|
87
|
+
if len(response) == 0: # No input; return the default
|
88
|
+
return options[-1]
|
89
|
+
elif len(response) == 1: # Single-character; find matching option
|
90
|
+
return next((opt for opt, char in zip(options, single_char_options) if char == response), options[-1])
|
91
|
+
else:
|
92
|
+
# Make sure response is a valid option; if not return the default
|
93
|
+
return next((opt for opt in options if opt == response), options[-1])
|
94
|
+
|
95
|
+
@staticmethod
|
96
|
+
def _get_user_input_dialog(prompt, options):
|
97
|
+
"""
|
98
|
+
Get input from the user using a dialog box.
|
99
|
+
:param prompt: The main prompt string to display to the user.
|
100
|
+
:param options: A tuple of options to present to the user for them to select; must have
|
101
|
+
at least one. The last option is the default.
|
102
|
+
:return: The user response, which is guaranteed to be one of the supplied ``options``
|
103
|
+
(if the user input an invalid response the default option is used).
|
104
|
+
"""
|
105
|
+
return OptionDialog.create_dialog_get_value(prompt, options)
|
106
|
+
|
62
107
|
@staticmethod
|
63
108
|
@spy_point(enabled_for_groups='bond_self_test',
|
64
109
|
require_agent_result=True,
|
110
|
+
excluded_keys=('extra_dialog_prompt','single_char_options'),
|
65
111
|
spy_result=True)
|
66
|
-
def
|
112
|
+
def _get_user_input(prompt, options, single_char_options, extra_dialog_prompt=''):
|
67
113
|
"""
|
68
|
-
|
114
|
+
Acquire input from the user. Defaults to using the console to ask for input. If
|
115
|
+
a console is not available, falls back to using a popup dialog window.
|
116
|
+
:param prompt: The main prompt string to display to the user.
|
117
|
+
:param options: A tuple of options to present to the user for them to select; must have
|
118
|
+
at least one. The last option is the default.
|
119
|
+
:param single_char_options: A tuple of the single-character versions of each of the
|
120
|
+
options supplied; this tuple must be the same length as ``options``,
|
121
|
+
and each single-character version must appear within the option itself.
|
122
|
+
:param extra_dialog_prompt: An extra message to display before the prompt only if the fallback
|
123
|
+
dialog box is used.
|
124
|
+
:return: The user response, which is guaranteed to be one of the supplied ``options`` (if the user
|
125
|
+
input an invalid response the default option is used).
|
69
126
|
"""
|
70
|
-
|
71
|
-
|
127
|
+
if sys.stdin.isatty(): # We use the console to retrieve input
|
128
|
+
return ReconcileTool._get_user_input_console(prompt, options, single_char_options)
|
129
|
+
else: # We use a dialog box to retrieve input
|
130
|
+
print('System console not found; using a dialog box to retrieve input instead.')
|
131
|
+
return ReconcileTool._get_user_input_dialog(extra_dialog_prompt + '\n' + prompt, options)
|
72
132
|
|
73
133
|
@staticmethod
|
74
134
|
@spy_point(enabled_for_groups='bond_self_test')
|
@@ -81,23 +141,26 @@ class ReconcileTool:
|
|
81
141
|
print(what)
|
82
142
|
|
83
143
|
@staticmethod
|
84
|
-
|
144
|
+
@spy_point(enabled_for_groups='bond_self_test', mock_only=True)
|
145
|
+
def _random_string():
|
85
146
|
"""
|
86
|
-
|
87
|
-
Return True if there are no diffs
|
147
|
+
Generate a short random string
|
88
148
|
"""
|
89
|
-
|
90
|
-
return (0 == ReconcileTool._invoke_command('diff -u -b "{0}" "{1}" >"{2}"'.format(reference_file,
|
91
|
-
current_file,
|
92
|
-
diff_file)))
|
149
|
+
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
|
93
150
|
|
94
151
|
@staticmethod
|
95
|
-
def
|
96
|
-
flavor):
|
152
|
+
def _tmp_file_name(flavor):
|
97
153
|
"""
|
98
|
-
The name of the
|
154
|
+
The name of the temporary file to use with a flavor-specific
|
155
|
+
(e.g. diff, curr, ref) ending.
|
99
156
|
"""
|
100
|
-
|
157
|
+
# We include a short random string to avoid potential collisions
|
158
|
+
return ReconcileTool.TMP_FILE_BASE_NAME + ReconcileTool._random_string() + "." + flavor
|
159
|
+
|
160
|
+
@staticmethod
|
161
|
+
@spy_point(enabled_for_groups='bond_self_test')
|
162
|
+
def _compute_diff(reference_lines, current_lines):
|
163
|
+
return list(difflib.unified_diff(reference_lines, current_lines, 'reference', 'current'))
|
101
164
|
|
102
165
|
def __init__(self):
|
103
166
|
pass
|
@@ -105,66 +168,60 @@ class ReconcileTool:
|
|
105
168
|
def reconcile(self,
|
106
169
|
test_name,
|
107
170
|
reference_file,
|
108
|
-
|
171
|
+
current_lines,
|
109
172
|
no_save=None):
|
110
173
|
"""
|
111
174
|
Reconcile the differences
|
112
175
|
@param test_name: the name of the test (for messages)
|
113
176
|
@param reference_file: the name of the reference observation file
|
114
|
-
@param
|
177
|
+
@param current_lines: a list of the lines which make up the current set of observations
|
115
178
|
:param no_save: if present, then disallows saving a new reference file.
|
116
179
|
This parameter should be a string explaining why saving is disallowed.
|
117
180
|
"""
|
118
181
|
|
119
|
-
if
|
182
|
+
if os.path.isfile(reference_file):
|
183
|
+
with open(reference_file, 'r') as f:
|
184
|
+
reference_lines = f.readlines()
|
185
|
+
else:
|
120
186
|
# if we do not have the reference file, pretend we have an empty one
|
121
187
|
ReconcileTool._print('WARNING: No reference observation file found for {}: {}'.format(test_name, reference_file))
|
122
|
-
|
123
|
-
with open(reference_file, 'w') as f:
|
124
|
-
pass
|
125
|
-
# We continue
|
188
|
+
reference_lines = list()
|
126
189
|
|
127
190
|
# Compute a quick difference
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
else:
|
146
|
-
ReconcileTool._print('Saving updated reference observation file for {}'.format(test_name))
|
147
|
-
shutil.move(merged_file, reference_file)
|
148
|
-
return True
|
191
|
+
unified_diff = self._compute_diff(reference_lines, current_lines)
|
192
|
+
|
193
|
+
if len(unified_diff) == 0:
|
194
|
+
# There are no differences
|
195
|
+
return True
|
196
|
+
|
197
|
+
# There are differences
|
198
|
+
merged_lines = self.invoke_tool(test_name,
|
199
|
+
reference_lines,
|
200
|
+
current_lines,
|
201
|
+
unified_diff,
|
202
|
+
no_save=no_save)
|
203
|
+
if merged_lines is not None:
|
204
|
+
# Accepted differences
|
205
|
+
if no_save:
|
206
|
+
ReconcileTool._print("Not saving reference observation file for {}: {}".format(test_name,
|
207
|
+
no_save))
|
149
208
|
else:
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
209
|
+
ReconcileTool._print('Saving updated reference observation file for {}'.format(test_name))
|
210
|
+
if os.path.isfile(reference_file):
|
211
|
+
os.unlink(reference_file)
|
212
|
+
with open(reference_file, 'w') as f:
|
213
|
+
f.writelines(merged_lines)
|
214
|
+
return True
|
215
|
+
else:
|
216
|
+
return False
|
158
217
|
|
159
218
|
def show_diff(self,
|
160
219
|
test_name,
|
161
|
-
|
220
|
+
unified_diff):
|
162
221
|
"""
|
163
|
-
Show the diff, and return it
|
222
|
+
Show the lines of the diff, and return it as a string
|
164
223
|
"""
|
165
|
-
diffs = ''
|
166
|
-
with open(diff_file, 'r') as f:
|
167
|
-
diffs = f.read()
|
224
|
+
diffs = ''.join(unified_diff)
|
168
225
|
if diffs:
|
169
226
|
ReconcileTool._print('There were differences in observations for {}: '.format(test_name))
|
170
227
|
ReconcileTool._print(diffs)
|
@@ -175,14 +232,14 @@ class ReconcileTool:
|
|
175
232
|
return diffs
|
176
233
|
|
177
234
|
def invoke_tool(self, test_name,
|
178
|
-
|
179
|
-
|
180
|
-
|
235
|
+
reference_lines,
|
236
|
+
current_lines,
|
237
|
+
unified_diff,
|
181
238
|
no_save=None):
|
182
239
|
"""
|
183
240
|
Invoke the actual tool
|
184
241
|
@param
|
185
|
-
@return either False, for a failed merge, or
|
242
|
+
@return either False, for a failed merge, or a list of the new lines to use as the reference
|
186
243
|
"""
|
187
244
|
assert False, 'Must override'
|
188
245
|
|
@@ -194,12 +251,12 @@ class ReconcileToolAbort(ReconcileTool):
|
|
194
251
|
|
195
252
|
def invoke_tool(self,
|
196
253
|
test_name,
|
197
|
-
|
198
|
-
|
199
|
-
|
254
|
+
reference_lines,
|
255
|
+
current_lines,
|
256
|
+
unified_diff,
|
200
257
|
no_save=None):
|
201
258
|
if not no_save:
|
202
|
-
self.show_diff(test_name,
|
259
|
+
self.show_diff(test_name, unified_diff)
|
203
260
|
ReconcileTool._print('Aborting (reconcile=abort) due to differences for test {}'.format(test_name))
|
204
261
|
return None
|
205
262
|
|
@@ -211,60 +268,70 @@ class ReconcileToolAccept(ReconcileTool):
|
|
211
268
|
|
212
269
|
def invoke_tool(self,
|
213
270
|
test_name,
|
214
|
-
|
215
|
-
|
216
|
-
|
271
|
+
reference_lines,
|
272
|
+
current_lines,
|
273
|
+
unified_diff,
|
217
274
|
no_save=None):
|
218
|
-
|
275
|
+
self.show_diff(test_name, unified_diff)
|
219
276
|
if not no_save:
|
220
277
|
ReconcileTool._print('Accepting (reconcile=accept) differences for test {}'.format(test_name))
|
221
|
-
return
|
278
|
+
return current_lines
|
222
279
|
|
223
280
|
|
224
281
|
class ReconcileToolConsole(ReconcileTool):
|
225
282
|
"""
|
226
|
-
Uses diff and console prompts to accept the changes
|
283
|
+
Uses diff and console prompts to accept the changes. Falls back to a dialog window
|
284
|
+
if no console is available for input.
|
227
285
|
"""
|
228
286
|
|
229
287
|
def invoke_tool(self,
|
230
288
|
test_name,
|
231
|
-
|
232
|
-
|
233
|
-
|
289
|
+
reference_lines,
|
290
|
+
current_lines,
|
291
|
+
unified_diff,
|
234
292
|
no_save=None):
|
235
293
|
|
294
|
+
extra_msg = None
|
236
295
|
while True:
|
237
296
|
if no_save:
|
238
|
-
prompt = 'Observations are shown for {}. Saving them not allowed because test failed.'
|
239
|
-
|
240
|
-
|
297
|
+
prompt = 'Observations are shown for {}. Saving them not allowed because test failed. ' \
|
298
|
+
'Use the diff option to show the differences.'.format(test_name)
|
299
|
+
response = self._input(prompt, ('kdiff3', 'diff', 'errors', 'continue'),
|
300
|
+
('k', 'd', 'e', 'c'),
|
301
|
+
extra_msg if extra_msg else '\n'.join(current_lines))
|
241
302
|
else:
|
242
303
|
# Show the diff
|
243
|
-
self.show_diff(test_name,
|
244
|
-
prompt = 'Do you want to accept the changes ({})
|
245
|
-
|
246
|
-
response = ReconcileTool._read_console(prompt)
|
304
|
+
diff = self.show_diff(test_name, unified_diff)
|
305
|
+
prompt = 'Do you want to accept the changes ({})?'.format(test_name)
|
306
|
+
response = self._input(prompt, ('kdiff3', 'yes', 'no'), ('k', 'y', 'n'), diff)
|
247
307
|
|
248
|
-
if response == '
|
249
|
-
return ReconcileToolKdiff3().invoke_tool(test_name,
|
308
|
+
if response == 'kdiff3':
|
309
|
+
return ReconcileToolKdiff3().invoke_tool(test_name, reference_lines, current_lines, unified_diff,
|
250
310
|
no_save=no_save)
|
251
|
-
|
252
|
-
|
253
|
-
self.show_diff(test_name, diff_file)
|
311
|
+
elif response == 'diff':
|
312
|
+
extra_msg = self.show_diff(test_name, unified_diff)
|
254
313
|
continue
|
255
|
-
|
256
|
-
|
257
|
-
ReconcileTool._print(
|
314
|
+
elif response == 'errors':
|
315
|
+
extra_msg = "Test {} had errors:\n{}".format(test_name, no_save)
|
316
|
+
ReconcileTool._print('\033[91m' + extra_msg + '\033[0m')
|
258
317
|
continue
|
259
|
-
|
260
|
-
if response == 'y' and not no_save:
|
318
|
+
elif response == 'yes' and not no_save:
|
261
319
|
ReconcileTool._print('Accepting differences for test {}'.format(test_name))
|
262
|
-
return
|
263
|
-
|
264
|
-
if not no_save:
|
320
|
+
return current_lines
|
321
|
+
elif not no_save:
|
265
322
|
ReconcileTool._print('Rejecting differences for test {}'.format(test_name))
|
266
323
|
return None
|
267
324
|
|
325
|
+
def _input(self, prompt, options, single_char_options, extra_dialog_prompt=None):
|
326
|
+
return ReconcileTool._get_user_input(prompt, options, single_char_options, extra_dialog_prompt)
|
327
|
+
|
328
|
+
|
329
|
+
class ReconcileToolDialog(ReconcileToolConsole):
|
330
|
+
"""
|
331
|
+
Merge with a dialog window.
|
332
|
+
"""
|
333
|
+
def _input(self, prompt, options, single_char_options, extra_dialog_prompt=None):
|
334
|
+
return ReconcileTool._get_user_input_dialog(extra_dialog_prompt + '\n' + prompt, options)
|
268
335
|
|
269
336
|
|
270
337
|
class ReconcileToolKdiff3(ReconcileTool):
|
@@ -274,57 +341,77 @@ class ReconcileToolKdiff3(ReconcileTool):
|
|
274
341
|
|
275
342
|
def invoke_tool(self,
|
276
343
|
test_name,
|
277
|
-
|
278
|
-
|
279
|
-
|
344
|
+
reference_lines,
|
345
|
+
current_lines,
|
346
|
+
unified_diff,
|
280
347
|
no_save=None):
|
281
348
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
349
|
+
# Save the current lines out to a temporary file to use with kdiff3
|
350
|
+
current_file = self._tmp_file_name('curr')
|
351
|
+
reference_file = self._tmp_file_name('ref')
|
352
|
+
try:
|
353
|
+
with open(current_file, 'w') as f:
|
354
|
+
f.writelines(current_lines)
|
355
|
+
with open(reference_file, 'w') as f:
|
356
|
+
f.writelines(reference_lines)
|
357
|
+
|
358
|
+
merged_file = None
|
359
|
+
if no_save:
|
360
|
+
response = ReconcileTool._get_user_input(
|
361
|
+
"\n!!! MERGING NOT ALLOWED for {}: {}. Want to start kdiff3?".format(test_name, no_save),
|
362
|
+
('yes', 'no'), ('y', 'n'))
|
363
|
+
if response != 'yes':
|
364
|
+
return None
|
365
|
+
|
366
|
+
cmd = ('kdiff3 "{reference_file}" --L1 "{test_name}_REFERENCE" '
|
367
|
+
'"{current_file}" --L2 "{test_name}_CURRENT" ').format(
|
368
|
+
reference_file=reference_file,
|
369
|
+
current_file=current_file,
|
370
|
+
test_name=test_name)
|
371
|
+
ReconcileTool._invoke_command(cmd)
|
287
372
|
return None
|
288
373
|
|
289
|
-
cmd = ('kdiff3 "{reference_file}" --L1 "{test_name}_REFERENCE" '
|
290
|
-
'"{current_file}" --L2 "{test_name}_CURRENT" ').format(
|
291
|
-
reference_file=reference_file,
|
292
|
-
current_file=current_file,
|
293
|
-
test_name=test_name)
|
294
|
-
else:
|
295
|
-
merged_file = self._aux_file_name(current_file, 'merged')
|
296
|
-
|
297
|
-
cmd = ('kdiff3 -m "{reference_file}" --L1 "{test_name}_REFERENCE" '
|
298
|
-
'"{current_file}" --L2 "{test_name}_CURRENT" '
|
299
|
-
' -o "{merged_file}"').format(reference_file=reference_file,
|
300
|
-
current_file=current_file,
|
301
|
-
merged_file=merged_file,
|
302
|
-
test_name=test_name)
|
303
|
-
|
304
|
-
print(cmd)
|
305
|
-
code = ReconcileTool._invoke_command(cmd)
|
306
|
-
if no_save:
|
307
|
-
return None
|
308
|
-
else:
|
309
|
-
if code == 0 :
|
310
|
-
# Merged ok
|
311
|
-
return merged_file
|
312
374
|
else:
|
313
|
-
|
314
|
-
|
315
|
-
|
375
|
+
merged_file = self._tmp_file_name('merged')
|
376
|
+
cmd = ('kdiff3 -m "{reference_file}" --L1 "{test_name}_REFERENCE" '
|
377
|
+
'"{current_file}" --L2 "{test_name}_CURRENT" '
|
378
|
+
' -o "{merged_file}"').format(reference_file=reference_file,
|
379
|
+
current_file=current_file,
|
380
|
+
merged_file=merged_file,
|
381
|
+
test_name=test_name)
|
382
|
+
|
383
|
+
code = ReconcileTool._invoke_command(cmd)
|
384
|
+
if code == 0:
|
385
|
+
# Merged ok
|
386
|
+
with open(merged_file, 'r') as f:
|
387
|
+
merged_lines = f.readlines()
|
388
|
+
message = 'Merge successful; saving a new reference file. '
|
389
|
+
ret = merged_lines
|
390
|
+
else:
|
391
|
+
message = 'Merge unsuccessful; not saving a new reference file. '
|
392
|
+
ret = None
|
393
|
+
|
394
|
+
ReconcileTool._get_user_input(message, ('continue',), ('c',))
|
395
|
+
return ret
|
396
|
+
finally:
|
397
|
+
if os.path.isfile(current_file):
|
398
|
+
os.unlink(current_file)
|
399
|
+
if os.path.isfile(reference_file):
|
400
|
+
os.unlink(reference_file)
|
401
|
+
if merged_file is not None and os.path.isfile(merged_file):
|
402
|
+
os.unlink(merged_file)
|
316
403
|
|
317
404
|
|
318
405
|
def reconcile_observations(settings,
|
319
406
|
test_name,
|
320
407
|
reference_file,
|
321
|
-
|
408
|
+
current_lines,
|
322
409
|
no_save=None):
|
323
410
|
"""
|
324
411
|
Reconcile the observations
|
325
412
|
:param settings: a settings object
|
326
413
|
:param reference_file: the reference file
|
327
|
-
:param
|
414
|
+
:param current_lines: a list of all of the lines in the current set of observations
|
328
415
|
:param no_save: If present, then saving of new references is not allowed. This parameter
|
329
416
|
should be a short string explaining why saving is not allowed.
|
330
417
|
:return:
|
@@ -333,7 +420,7 @@ def reconcile_observations(settings,
|
|
333
420
|
reconcile_tool = ReconcileTool.select(settings.get('reconcile'))
|
334
421
|
return reconcile_tool.reconcile(test_name,
|
335
422
|
reference_file,
|
336
|
-
|
423
|
+
current_lines,
|
337
424
|
no_save=no_save)
|
338
425
|
|
339
426
|
|
@@ -361,6 +448,9 @@ if __name__ == '__main__':
|
|
361
448
|
print('The current file does not exist: {}'.format(opts.current), file=sys.stderr)
|
362
449
|
sys.exit(1)
|
363
450
|
|
451
|
+
with open(opts.current, 'r') as f:
|
452
|
+
current_lines = f.readlines()
|
453
|
+
os.unlink(opts.current)
|
364
454
|
|
365
455
|
# Guess the test name from the current file
|
366
456
|
if opts.test:
|
@@ -378,7 +468,7 @@ if __name__ == '__main__':
|
|
378
468
|
if reconcile_observations(main_settings,
|
379
469
|
main_test_name,
|
380
470
|
opts.reference,
|
381
|
-
|
471
|
+
current_lines,
|
382
472
|
no_save=opts.no_save):
|
383
473
|
sys.exit(0)
|
384
474
|
else:
|