bond-spy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE +27 -0
- data/README.rst +65 -0
- data/Rakefile +6 -0
- data/bin/bond_reconcile.py +385 -0
- data/bin/setup +5 -0
- data/bond.gemspec +36 -0
- data/lib/bond.rb +469 -0
- data/lib/bond/spec_helper.rb +32 -0
- data/lib/bond/targetable.rb +210 -0
- data/lib/bond/version.rb +3 -0
- data/spec/bond_spec.rb +158 -0
- data/spec/bond_targetable_spec.rb +202 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/test_observations/.gitignore +2 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_call_doers_before_returning_result.json +14 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_call_the_function_passed_as_result_if_it_is_callable.json +23 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_a_single_doer_if_filter_criteria_are_met.json +10 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_correctly_call_multiple_doers.json +13 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_not_call_doers_of_overriden_agents.json +8 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_override_old_agents_with_newer_agents.json +0 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_an_exception_if_specified_by_agent.json +9 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_throw_the_result_of_the_value_passed_to_exception_if_callable.json +10 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_should_work_with_multiple_agents_for_different_spy_points.json +23 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_override_old_agents_with_newer_agents_unless_theott8glo1xn.json +22 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_combinations_of_filters.json +37 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_function_filters.json +16 -0
- data/spec/test_observations/bond_spec/Bond_with_agents_with_filters_should_respect_single_key_value_filters_of_all_types.json +121 -0
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_nested_hashes_and_arrays_with_hash_sorting.json +31 -0
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_with_a_spy_point_name.json +12 -0
- data/spec/test_observations/bond_spec/Bond_without_any_agents_should_correctly_log_some_normal_arguments_without_a_spy_point_name.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_continue_is_returned.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_continues_to_the_method_when_agent_result_none_is_returned.json +14 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_passes_through_blocks.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_returns_nil__and_mocks__when_an_agent_returns_nil.json +11 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_private_methods.json +6 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_correctly_spies_protected_methods.json +6 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_class_method.json +7 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_method_with_variabl19nhijeqoo.json +13 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_positional_a6qc3d4el92.json +33 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_positional_aott8glo1xn.json +20 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_mix_of_required_andbcgjq06had.json +17 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_a_normal_method.json +7 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_all_optional_keyword_arguments.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_argument_types_correctly_spies_on_variable_keyword_arguments.json +22 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_changes_the_spy_point_naj4gnwvcu8n.json +5 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_errors_when_mocking_is_r9j7wklng0z.json +6 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_ignores_excluded_keys.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_mocks_when_one_is_specified.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_spies_the_return_value_w19nhijeqoo.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_different_spy_point_parameters_correctly_spies_the_return_value_ww8esw1qdxc.json +10 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_included_module_methods.json +6 -0
- data/spec/test_observations/bond_targetable_spec/BondTargetable_with_modules_correctly_spies_on_module_methods.json +7 -0
- data/tutorials/binary_search_tree/bst.rb +120 -0
- data/tutorials/binary_search_tree/bst_spec.rb +82 -0
- data/tutorials/binary_search_tree/run_tests.sh +4 -0
- data/tutorials/binary_search_tree/test_observations/.gitignore +2 -0
- data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_add_nodes_to_the_BST_correctly__testing_with_Bond.json +20 -0
- data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_add_nodes_to_the_BST_correctly__testing_without_Bond.json +3 -0
- data/tutorials/binary_search_tree/test_observations/bst_spec/Node_should_correctly_delete_nodes_from_the_BST.json +29 -0
- data/tutorials/heat_watcher/heat_watcher.rb +107 -0
- data/tutorials/heat_watcher/heat_watcher_spec.rb +116 -0
- data/tutorials/heat_watcher/run_tests.sh +4 -0
- data/tutorials/heat_watcher/test_observations/.gitignore +2 -0
- data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_critical_errors.json +142 -0
- data/tutorials/heat_watcher/test_observations/heat_watcher_spec/HeatWatcher_should_properly_report_warnings_and_switch_back_to_OK_status.json +132 -0
- metadata +211 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff046742322ed31d8ac6ce9b5145d67072af7593
|
4
|
+
data.tar.gz: 95c911f15617180b17afc73b0812c56225cb8112
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e168396ccc1ab5a002be950f5680b26b31a26186ef0e6003b3bd54bc511591973367128683d2da958627ce3ba65e2dfbb1db71c68f66851d19ff174c9b4f5b56
|
7
|
+
data.tar.gz: f6511ca661d7395403668d315dfbc96f47b9578d1757528a8c25b47e1e2dfe56b963590fa237643e870788cc5bb8ed1095bfb8eec8972adec535c42b4acf398b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown --no-private --protected
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2015, Conviva Inc.
|
2
|
+
Copyright (c) 2015, George Necula, Erik Krogen
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
15
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
16
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
18
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
19
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
20
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
21
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
22
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
23
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
The views and conclusions contained in the software and documentation are those
|
26
|
+
of the authors and should not be interpreted as representing official policies,
|
27
|
+
either expressed or implied, of the FreeBSD Project.
|
data/README.rst
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
Bond
|
2
|
+
.......................
|
3
|
+
|
4
|
+
For more about Bond and usage instructions, see the main `documentation page <http://necula01.github.io/bond/>`_.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
-----------------------
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
.. code::
|
12
|
+
|
13
|
+
gem 'bond-spy'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
.. code:: bash
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
.. code:: bash
|
24
|
+
|
25
|
+
$ gem install bond-spy
|
26
|
+
|
27
|
+
Please note that the minimum version of Ruby that Bond supports is 2.1.
|
28
|
+
|
29
|
+
Development
|
30
|
+
-----------------------
|
31
|
+
|
32
|
+
After checking out the repo, run ``bin/setup`` to install dependencies. Then, run ``rake spec``
|
33
|
+
to run the tests.
|
34
|
+
|
35
|
+
API documentation is built using YARD; simply running ``yard`` will generate the documentation.
|
36
|
+
However, for the full suite of documentation including examples, you should build from the
|
37
|
+
project main directory (one level up), which will generate and integrate YARD documentation.
|
38
|
+
To do this, simply run ``make docs`` from the main directory.
|
39
|
+
|
40
|
+
.. rst_newVersionInstructionsStart
|
41
|
+
|
42
|
+
To install this gem onto your local machine, run ``bundle exec rake install``. To release a
|
43
|
+
new version, update the version number in ``bond/version.rb`` and the date in ``bond.gemspec``,
|
44
|
+
commit your changes, and tag the commit with ``rbond-vX.X.X``. Please include '[rbond]' as the
|
45
|
+
first part of any commit message related to the Ruby version of Bond to differentiate it from
|
46
|
+
commits related to other parts of Bond. Push your commit up (including tags), build the gem
|
47
|
+
using ``gem build bond.gemspec``, and run ``gem push bond-spy-X.X.X.gem`` to release the .gem
|
48
|
+
file to `rubygems.org <https://rubygems.org>`_. For example:
|
49
|
+
|
50
|
+
.. code:: bash
|
51
|
+
|
52
|
+
$ git commit -am "[rbond] Release version 1.0.2"
|
53
|
+
$ git tag rbond-v1.0.2
|
54
|
+
$ git push origin --tags
|
55
|
+
$ git push origin
|
56
|
+
$ gem build bond.gemspec
|
57
|
+
$ gem push bond-spy-1.0.2.gem
|
58
|
+
|
59
|
+
.. rst_newVersionInstructionsEnd
|
60
|
+
|
61
|
+
Contributing
|
62
|
+
-----------------------
|
63
|
+
|
64
|
+
Bug reports and pull requests are welcome on `GitHub <https://github.com/necula01/bond>`_.
|
65
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,385 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
from __future__ import print_function
|
4
|
+
|
5
|
+
import os
|
6
|
+
import re
|
7
|
+
import shutil
|
8
|
+
import sys
|
9
|
+
|
10
|
+
try:
|
11
|
+
# Import bond safely
|
12
|
+
from bond import spy_point
|
13
|
+
except ImportError:
|
14
|
+
spy_point = lambda **kw: lambda f: f
|
15
|
+
|
16
|
+
|
17
|
+
"""
|
18
|
+
Reconcile the reference observations with the current ones
|
19
|
+
"""
|
20
|
+
|
21
|
+
|
22
|
+
class ReconcileTool:
|
23
|
+
"""
|
24
|
+
Base class for reconcile tools
|
25
|
+
"""
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def select(reconcile_tool=None):
|
29
|
+
|
30
|
+
if reconcile_tool == 'accept':
|
31
|
+
return ReconcileToolAccept()
|
32
|
+
|
33
|
+
if reconcile_tool == 'abort':
|
34
|
+
return ReconcileToolAbort()
|
35
|
+
|
36
|
+
if reconcile_tool == 'console':
|
37
|
+
return ReconcileToolConsole()
|
38
|
+
|
39
|
+
if reconcile_tool == 'kdiff3':
|
40
|
+
return ReconcileToolKdiff3()
|
41
|
+
|
42
|
+
if reconcile_tool is None:
|
43
|
+
# Look at the environment variable BOND_RECONCILE
|
44
|
+
reconcile_tool = os.environ.get('BOND_RECONCILE', 'abort')
|
45
|
+
if reconcile_tool is not None:
|
46
|
+
return ReconcileTool.select(reconcile_tool)
|
47
|
+
|
48
|
+
assert False, 'Unrecognized bond_reconcile tool name: {}'.format(reconcile_tool)
|
49
|
+
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
@spy_point(enabled_for_groups='bond_self_test',
|
53
|
+
require_agent_result=True)
|
54
|
+
def _invoke_command(cmd):
|
55
|
+
"""
|
56
|
+
Invoke a shell command. Return the exit code.
|
57
|
+
:param cmd:
|
58
|
+
:return:
|
59
|
+
"""
|
60
|
+
return os.system(cmd)
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
@spy_point(enabled_for_groups='bond_self_test',
|
64
|
+
require_agent_result=True,
|
65
|
+
spy_result=True)
|
66
|
+
def _read_console(prompt):
|
67
|
+
"""
|
68
|
+
A function to read from the console
|
69
|
+
"""
|
70
|
+
return raw_input(prompt)
|
71
|
+
|
72
|
+
|
73
|
+
@staticmethod
|
74
|
+
@spy_point(enabled_for_groups='bond_self_test')
|
75
|
+
def _print(what):
|
76
|
+
"""
|
77
|
+
A function to do the printing, so we can spy on it
|
78
|
+
:param what:
|
79
|
+
:return:
|
80
|
+
"""
|
81
|
+
print(what)
|
82
|
+
|
83
|
+
@staticmethod
|
84
|
+
def _quick_diff(reference_file, current_file, diff_file):
|
85
|
+
"""
|
86
|
+
Compute a diff between files and save it into a diff_file.
|
87
|
+
Return True if there are no diffs
|
88
|
+
"""
|
89
|
+
# TODO: implicit dependency on a 'diff' command line tool with the same usage syntax that you're expecting
|
90
|
+
return (0 == ReconcileTool._invoke_command('diff -u -b "{0}" "{1}" >"{2}"'.format(reference_file,
|
91
|
+
current_file,
|
92
|
+
diff_file)))
|
93
|
+
|
94
|
+
@staticmethod
|
95
|
+
def _aux_file_name(current_file,
|
96
|
+
flavor):
|
97
|
+
"""
|
98
|
+
The name of the auxiliary (e.g., diff, merged) file to use
|
99
|
+
"""
|
100
|
+
return current_file + "." + flavor
|
101
|
+
|
102
|
+
def __init__(self):
|
103
|
+
pass
|
104
|
+
|
105
|
+
def reconcile(self,
|
106
|
+
test_name,
|
107
|
+
reference_file,
|
108
|
+
current_file,
|
109
|
+
no_save=None):
|
110
|
+
"""
|
111
|
+
Reconcile the differences
|
112
|
+
@param test_name: the name of the test (for messages)
|
113
|
+
@param reference_file: the name of the reference observation file
|
114
|
+
@param current_file: the name of the current observation file
|
115
|
+
:param no_save: if present, then disallows saving a new reference file.
|
116
|
+
This parameter should be a string explaining why saving is disallowed.
|
117
|
+
"""
|
118
|
+
|
119
|
+
if not os.path.isfile(reference_file):
|
120
|
+
# if we do not have the reference file, pretend we have an empty one
|
121
|
+
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
|
126
|
+
|
127
|
+
# Compute a quick difference
|
128
|
+
diff_file = self._aux_file_name(current_file, "diff")
|
129
|
+
try:
|
130
|
+
if self._quick_diff(reference_file, current_file, diff_file):
|
131
|
+
# There are no differences
|
132
|
+
return True
|
133
|
+
|
134
|
+
# There are differences
|
135
|
+
merged_file = self.invoke_tool(test_name,
|
136
|
+
reference_file,
|
137
|
+
current_file,
|
138
|
+
diff_file,
|
139
|
+
no_save=no_save)
|
140
|
+
if merged_file is not None:
|
141
|
+
# Accepted differences
|
142
|
+
if no_save:
|
143
|
+
ReconcileTool._print("Not saving reference observation file for {}: {}".format(test_name,
|
144
|
+
no_save))
|
145
|
+
else:
|
146
|
+
ReconcileTool._print('Saving updated reference observation file for {}'.format(test_name))
|
147
|
+
shutil.move(merged_file, reference_file)
|
148
|
+
return True
|
149
|
+
else:
|
150
|
+
return False
|
151
|
+
|
152
|
+
finally:
|
153
|
+
# Delete the files
|
154
|
+
if os.path.isfile(diff_file):
|
155
|
+
os.unlink(diff_file)
|
156
|
+
if os.path.isfile(current_file):
|
157
|
+
os.unlink(current_file)
|
158
|
+
|
159
|
+
def show_diff(self,
|
160
|
+
test_name,
|
161
|
+
diff_file):
|
162
|
+
"""
|
163
|
+
Show the diff, and return it
|
164
|
+
"""
|
165
|
+
diffs = ''
|
166
|
+
with open(diff_file, 'r') as f:
|
167
|
+
diffs = f.read()
|
168
|
+
if diffs:
|
169
|
+
ReconcileTool._print('There were differences in observations for {}: '.format(test_name))
|
170
|
+
ReconcileTool._print(diffs)
|
171
|
+
# Print it again at the end; makes it easy to see in the console what test just failed
|
172
|
+
ReconcileTool._print('There were differences in observations for {}: '.format(test_name))
|
173
|
+
else:
|
174
|
+
ReconcileTool._print('No differences in observations for {}: '.format(test_name))
|
175
|
+
return diffs
|
176
|
+
|
177
|
+
def invoke_tool(self, test_name,
|
178
|
+
reference_file,
|
179
|
+
current_file,
|
180
|
+
diff_file,
|
181
|
+
no_save=None):
|
182
|
+
"""
|
183
|
+
Invoke the actual tool
|
184
|
+
@param
|
185
|
+
@return either False, for a failed merge, or the name of the file to use as the new reference
|
186
|
+
"""
|
187
|
+
assert False, 'Must override'
|
188
|
+
|
189
|
+
|
190
|
+
class ReconcileToolAbort(ReconcileTool):
|
191
|
+
"""
|
192
|
+
Abort all merges
|
193
|
+
"""
|
194
|
+
|
195
|
+
def invoke_tool(self,
|
196
|
+
test_name,
|
197
|
+
reference_file,
|
198
|
+
current_file,
|
199
|
+
diff_file,
|
200
|
+
no_save=None):
|
201
|
+
if not no_save:
|
202
|
+
self.show_diff(test_name, diff_file)
|
203
|
+
ReconcileTool._print('Aborting (reconcile=abort) due to differences for test {}'.format(test_name))
|
204
|
+
return None
|
205
|
+
|
206
|
+
|
207
|
+
class ReconcileToolAccept(ReconcileTool):
|
208
|
+
"""
|
209
|
+
Accept all changes
|
210
|
+
"""
|
211
|
+
|
212
|
+
def invoke_tool(self,
|
213
|
+
test_name,
|
214
|
+
reference_file,
|
215
|
+
current_file,
|
216
|
+
diff_file,
|
217
|
+
no_save=None):
|
218
|
+
diffs = self.show_diff(test_name, diff_file)
|
219
|
+
if not no_save:
|
220
|
+
ReconcileTool._print('Accepting (reconcile=accept) differences for test {}'.format(test_name))
|
221
|
+
return current_file
|
222
|
+
|
223
|
+
|
224
|
+
class ReconcileToolConsole(ReconcileTool):
|
225
|
+
"""
|
226
|
+
Uses diff and console prompts to accept the changes
|
227
|
+
"""
|
228
|
+
|
229
|
+
def invoke_tool(self,
|
230
|
+
test_name,
|
231
|
+
reference_file,
|
232
|
+
current_file,
|
233
|
+
diff_file,
|
234
|
+
no_save=None):
|
235
|
+
|
236
|
+
while True:
|
237
|
+
if no_save:
|
238
|
+
prompt = 'Observations are shown for {}. Saving them not allowed because test failed.'.format(
|
239
|
+
test_name,
|
240
|
+
) + ' Use the diff option to show the differences. ([k]diff3 | [d]iff | [e] errors | *): '
|
241
|
+
else:
|
242
|
+
# Show the diff
|
243
|
+
self.show_diff(test_name, diff_file)
|
244
|
+
prompt = 'Do you want to accept the changes ({}) ? ( [y]es | [k]diff3 | *): '.format(test_name)
|
245
|
+
|
246
|
+
response = ReconcileTool._read_console(prompt)
|
247
|
+
|
248
|
+
if response == 'k':
|
249
|
+
return ReconcileToolKdiff3().invoke_tool(test_name, reference_file, current_file, diff_file,
|
250
|
+
no_save=no_save)
|
251
|
+
|
252
|
+
if response == 'd' and no_save:
|
253
|
+
self.show_diff(test_name, diff_file)
|
254
|
+
continue
|
255
|
+
|
256
|
+
if response == 'e' and no_save:
|
257
|
+
ReconcileTool._print("Test {} had errors:\n{}".format(test_name, no_save))
|
258
|
+
continue
|
259
|
+
|
260
|
+
if response == 'y' and not no_save:
|
261
|
+
ReconcileTool._print('Accepting differences for test {}'.format(test_name))
|
262
|
+
return current_file
|
263
|
+
|
264
|
+
if not no_save:
|
265
|
+
ReconcileTool._print('Rejecting differences for test {}'.format(test_name))
|
266
|
+
return None
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
class ReconcileToolKdiff3(ReconcileTool):
|
271
|
+
"""
|
272
|
+
Merge with kdiff3
|
273
|
+
"""
|
274
|
+
|
275
|
+
def invoke_tool(self,
|
276
|
+
test_name,
|
277
|
+
reference_file,
|
278
|
+
current_file,
|
279
|
+
diff_file,
|
280
|
+
no_save=None):
|
281
|
+
|
282
|
+
if no_save:
|
283
|
+
response = ReconcileTool._read_console(
|
284
|
+
"\n!!! MERGING NOT ALLOWED for {}: {}. Want to start kdiff3? ([y] | *): ".format(test_name,
|
285
|
+
no_save))
|
286
|
+
if response != "y":
|
287
|
+
return None
|
288
|
+
|
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
|
+
else:
|
313
|
+
if os.path.isfile(merged_file):
|
314
|
+
os.unlink(merged_file)
|
315
|
+
return None
|
316
|
+
|
317
|
+
|
318
|
+
def reconcile_observations(settings,
|
319
|
+
test_name,
|
320
|
+
reference_file,
|
321
|
+
current_file,
|
322
|
+
no_save=None):
|
323
|
+
"""
|
324
|
+
Reconcile the observations
|
325
|
+
:param settings: a settings object
|
326
|
+
:param reference_file: the reference file
|
327
|
+
:param current_file: the current file with observations
|
328
|
+
:param no_save: If present, then saving of new references is not allowed. This parameter
|
329
|
+
should be a short string explaining why saving is not allowed.
|
330
|
+
:return:
|
331
|
+
"""
|
332
|
+
|
333
|
+
reconcile_tool = ReconcileTool.select(settings.get('reconcile'))
|
334
|
+
return reconcile_tool.reconcile(test_name,
|
335
|
+
reference_file,
|
336
|
+
current_file,
|
337
|
+
no_save=no_save)
|
338
|
+
|
339
|
+
|
340
|
+
if __name__ == '__main__':
|
341
|
+
import optparse
|
342
|
+
|
343
|
+
optParser = optparse.OptionParser(usage=os.path.basename(__file__),
|
344
|
+
description='Compare and reconciles differences in Bond observation files')
|
345
|
+
|
346
|
+
optParser.add_option('--reconcile', dest='reconcile', action='store', default=None,
|
347
|
+
help='The reconcile tool to use. Available: accept, abort, console, kdiff3.')
|
348
|
+
optParser.add_option('--reference', dest='reference', action='store', default=None,
|
349
|
+
help='The reference observation file')
|
350
|
+
optParser.add_option('--current', dest='current', action='store', default=None,
|
351
|
+
help='The current observation file')
|
352
|
+
optParser.add_option('--test', dest='test', action='store', default=None,
|
353
|
+
help='The name of the test (for UI). Default is to extract from --current')
|
354
|
+
optParser.add_option('--no-save', dest='no_save', action='store', default=None,
|
355
|
+
help='If given, the reason why saving of new references is not allowed')
|
356
|
+
(opts, args) = optParser.parse_args()
|
357
|
+
if opts.reference is None:
|
358
|
+
sys.exit(1)
|
359
|
+
|
360
|
+
if opts.current is None or not os.path.isfile(opts.current):
|
361
|
+
print('The current file does not exist: {}'.format(opts.current), file=sys.stderr)
|
362
|
+
sys.exit(1)
|
363
|
+
|
364
|
+
|
365
|
+
# Guess the test name from the current file
|
366
|
+
if opts.test:
|
367
|
+
main_test_name = opts.test
|
368
|
+
else:
|
369
|
+
main_test_name = os.path.splitext(os.path.basename(opts.current))[0]
|
370
|
+
|
371
|
+
if opts.reconcile:
|
372
|
+
main_reconcile = opts.reconcile
|
373
|
+
else:
|
374
|
+
main_reconcile = os.environ.get('BOND_RECONCILE', 'console')
|
375
|
+
|
376
|
+
main_settings = dict(reconcile=main_reconcile)
|
377
|
+
|
378
|
+
if reconcile_observations(main_settings,
|
379
|
+
main_test_name,
|
380
|
+
opts.reference,
|
381
|
+
opts.current,
|
382
|
+
no_save=opts.no_save):
|
383
|
+
sys.exit(0)
|
384
|
+
else:
|
385
|
+
sys.exit(1)
|