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.
- 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)
|