Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved diff and properties #99

Closed
wants to merge 12 commits into from
Closed
40 changes: 34 additions & 6 deletions svn/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,13 @@ def properties(self, rel_path=None):
# query the proper list of this path
root = xml.etree.ElementTree.fromstring(result)
target_elem = root.find('target')
property_names = [p.attrib["name"]
for p in target_elem.findall('property')]
if target_elem is not None:
property_names = [p.attrib["name"]
for p in target_elem.findall('property')]
# In the event that no properties are associated, an empty properties
# element is returned as the result.
else:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this the case?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any time there are no properties associated, the "target" element it's searching for won't be found.
Examples:

http://svn.apache.org/repos/asf/activemq/activecluster/trunk/src/main/java/org/apache/activecluster@1808406
<?xml version="1.0" encoding="UTF-8"?>
<properties>
</properties>

http://svn.apache.org/repos/asf/activemq/activecluster/trunk/src/main/java/org/apache/activecluster/Cluster.java@1808406
<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <target path="http://svn.apache.org/repos/asf/activemq/activecluster/trunk/src/main/java/org/apache/activecluster/Cluster.java">
    <property name="svn:eol-style"/>
  </target>
</properties>

Copy link
Owner

@dsoprea dsoprea Sep 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I got it. Thanks for clarifying. You're absolutely sure that you can't have more than one target? Seems like you could.

property_names = []

# now query the content of each propery
property_dict = {}
Expand Down Expand Up @@ -449,13 +454,36 @@ def diff(self, old, new, rel_path=None):
'--new', '{0}@{1}'.format(full_url_or_path, new)],
do_combine=True)
file_to_diff = {}

# A diff has this form, potentially repeating for multiple files.
#
# Index: relative/filename.txt
# ===================================================================
# The diff content
#
# Here we split diffs into files by the index section, pick up the
# file name, then split again to pick up the content.
for non_empty_diff in filter(None, diff_result.decode('utf8').split('Index: ')):
split_diff = non_empty_diff.split('==')
file_to_diff[split_diff[0].strip().strip('/')] = split_diff[-1].strip('=').strip()
split_diff = non_empty_diff.split('='*67)
index_filename = split_diff[0].strip().strip('/')
file_to_diff[index_filename] = split_diff[-1].strip()
diff_summaries = self.diff_summary(old, new, rel_path)

# Allocate summary info to the text for each change.
# Not all diffs necessarily affect file text (directory changes, for example).
for diff_summary in diff_summaries:
diff_summary['diff'] = \
file_to_diff[diff_summary['path'].split(full_url_or_path)[-1].strip('/')]
diff_summary['diff'] = ''
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section could use some commentary. Also, can you also refactor the split() to use startswith() or searching instead? I realize you weren't responsible for this, but the way it is is far from intuitive. Though I can surmise what we're doing here, it's not obvious when the if's will or will not trigger.

if 'path' in diff_summary:
# Summary file paths are absolute, while diff file indexes are relative.
# We try to match them up, first checking the root of the diff.
summary_index = diff_summary['path'][len(full_url_or_path):].strip('/')
# If the diff was conducted directly on the file, not a directory, the
# above will fail to find the relative file name, so we look for that directly.
if summary_index == '':
summary_index = os.path.basename(full_url_or_path)
# If we can match a diff to a summary, we attach it.
if summary_index in file_to_diff:
diff_summary['diff'] = file_to_diff[summary_index]
return diff_summaries

@property
Expand Down
7 changes: 6 additions & 1 deletion tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ def test_create_repository(self):
a.create(temp_path)

# Do a read.
rc = svn.remote.RemoteClient('file://' + temp_path)
# Duck-type identification of path type (Windows, Linux, etc.) to ensure
# file uri canonicity is enforced. URI must be file:///.
if temp_path[0] == '/':
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you place a comment indicating that this is the duck-typing approach to detecting Windows versus non-Windows?

rc = svn.remote.RemoteClient('file://' + temp_path)
else:
rc = svn.remote.RemoteClient('file:///' + temp_path)
info = rc.info()
_LOGGER.debug("Info from new repository: [%s]", str(info))
finally:
Expand Down
30 changes: 29 additions & 1 deletion tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ def __setup_test_environment(self):
self.__temp_co_path = self.__get_temp_path_to_use()
print("CO_PATH: {}".format(self.__temp_co_path))

r = svn.remote.RemoteClient('file://' + self.__temp_repo_path)
# Duck-type identification of path type (Windows, Linux, etc.) to ensure
# file uri canonicity is enforced. URI must be file:///.
if self.__temp_repo_path[0] == '/':
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above comment.

r = svn.remote.RemoteClient('file://' + self.__temp_repo_path)
else:
r = svn.remote.RemoteClient('file:///' + self.__temp_repo_path)
r.checkout(self.__temp_co_path)

# Create a client for it.
Expand Down Expand Up @@ -222,6 +227,18 @@ def test_diff(self):
'http://svn.apache.org/repos/asf/sling/trunk/pom.xml' \
in individual_diff[diff_key])

def test_diff_separator(self):
"""
Checking diff
:return:
"""
cc = self.__get_cc()
change = '502054'
file_cluster_event = 'activemq/activecluster/trunk/src/main/java/org/apache/activecluster/ClusterEvent.java'
actual_answer = cc.diff('0', change, file_cluster_event)[0]
self.assertTrue('static final int UPDATE_NODE = 2' in actual_answer['diff'])
self.assertTrue('else if (type == FAILED_NODE)' in actual_answer['diff'])

def test_list(self):
"""
Checking list
Expand Down Expand Up @@ -311,5 +328,16 @@ def test_force_export(self):
except svn.exception.SvnException:
self.fail("SvnException raised with force export")

def test_properties(self):
"""
Checking we can retrieve properties
:return:
"""
cc = self.__get_cc()
props = cc.properties('activemq/activecluster/trunk/src/main/java/org/apache/activecluster@1808406')
self.assertFalse(bool(props))
props = cc.properties('activemq/activecluster/trunk/src/main/java/org/apache/activecluster/Cluster.java@1808406')
self.assertEqual(props['svn:eol-style'], 'native')

def test_cleanup(self):
self.__temp_lc.cleanup()