##// END OF EJS Templates
repo: Implement ReadmeFinder...
johbo -
r772:c191de56 default
parent child Browse files
Show More
@@ -0,0 +1,106 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.lib.vcs import nodes
24 from rhodecode.model.repo import ReadmeFinder
25
26
27 @pytest.fixture
28 def commit_util(vcsbackend_stub):
29 """
30 Provide a commit which has certain files in it's tree.
31
32 This is based on the fixture "vcsbackend" and will automatically be
33 parametrized for all vcs backends.
34 """
35 return CommitUtility(vcsbackend_stub)
36
37
38 class CommitUtility:
39
40 def __init__(self, vcsbackend):
41 self.vcsbackend = vcsbackend
42
43 def commit_with_files(self, filenames):
44 commits = [
45 {'message': 'Adding all requested files',
46 'added': [
47 nodes.FileNode(filename, content='')
48 for filename in filenames
49 ]}]
50 repo = self.vcsbackend.create_repo(commits=commits)
51 return repo.get_commit()
52
53
54 def test_no_matching_file_returns_none(commit_util):
55 commit = commit_util.commit_with_files(['LIESMICH'])
56 finder = ReadmeFinder(default_renderer='rst')
57 filename = finder.search(commit)
58 assert filename is None
59
60
61 def test_matching_file_returns_the_file_name(commit_util):
62 commit = commit_util.commit_with_files(['README'])
63 finder = ReadmeFinder(default_renderer='rst')
64 filename = finder.search(commit)
65 assert filename == 'README'
66
67
68 def test_matching_file_with_extension(commit_util):
69 commit = commit_util.commit_with_files(['README.rst'])
70 finder = ReadmeFinder(default_renderer='rst')
71 filename = finder.search(commit)
72 assert filename == 'README.rst'
73
74
75 def test_prefers_readme_without_extension(commit_util):
76 commit = commit_util.commit_with_files(['README.rst', 'Readme'])
77 finder = ReadmeFinder()
78 filename = finder.search(commit)
79 assert filename == 'Readme'
80
81
82 @pytest.mark.parametrize('renderer, expected', [
83 ('rst', 'readme.rst'),
84 ('markdown', 'readme.md'),
85 ])
86 def test_prefers_renderer_extensions(commit_util, renderer, expected):
87 commit = commit_util.commit_with_files(
88 ['readme.rst', 'readme.md', 'readme.txt'])
89 finder = ReadmeFinder(default_renderer=renderer)
90 filename = finder.search(commit)
91 assert filename == expected
92
93
94 def test_finds_readme_in_subdirectory(commit_util):
95 commit = commit_util.commit_with_files(['doc/README.rst', 'LIESMICH'])
96 finder = ReadmeFinder()
97 filename = finder.search(commit)
98 assert filename == 'doc/README.rst'
99
100
101 def test_prefers_subdirectory_with_priority(commit_util):
102 commit = commit_util.commit_with_files(
103 ['Doc/Readme.rst', 'Docs/Readme.rst'])
104 finder = ReadmeFinder()
105 filename = finder.search(commit)
106 assert filename == 'Doc/Readme.rst'
@@ -947,10 +947,97 b' class ReadmeFinder:'
947 different.
947 different.
948 """
948 """
949
949
950 def __init__(self, default_renderer):
950 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
951 path_re = re.compile(r'^docs?', re.IGNORECASE)
952
953 default_priorities = {
954 None: 0,
955 '.text': 2,
956 '.txt': 3,
957 '.rst': 1,
958 '.rest': 2,
959 '.md': 1,
960 '.mkdn': 2,
961 '.mdown': 3,
962 '.markdown': 4,
963 }
964
965 path_priority = {
966 'doc': 0,
967 'docs': 1,
968 }
969
970 FALLBACK_PRIORITY = 99
971
972 RENDERER_TO_EXTENSION = {
973 'rst': ['.rst', '.rest'],
974 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
975 }
976
977 def __init__(self, default_renderer=None):
951 self._default_renderer = default_renderer
978 self._default_renderer = default_renderer
979 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
980 default_renderer, [])
952
981
953 def search(self, commit):
982 def search(self, commit, path='/'):
983 """
984 Find a readme in the given `commit`.
985 """
986 nodes = commit.get_nodes(path)
987 matches = self._match_readmes(nodes)
988 matches = self._sort_according_to_priority(matches)
989 if matches:
990 return matches[0].path
991
992 paths = self._match_paths(nodes)
993 paths = self._sort_paths_according_to_priority(paths)
994 for path in paths:
995 match = self.search(commit, path=path)
996 if match:
997 return match
998
999 return None
1000
1001 def _match_readmes(self, nodes):
1002 for node in nodes:
1003 if not node.is_file():
1004 continue
1005 path = node.path.rsplit('/', 1)[-1]
1006 match = self.readme_re.match(path)
1007 if match:
1008 extension = match.group(1)
1009 yield ReadmeMatch(node, match, self._priority(extension))
1010
1011 def _match_paths(self, nodes):
1012 for node in nodes:
1013 if not node.is_dir():
1014 continue
1015 match = self.path_re.match(node.path)
1016 if match:
1017 yield node.path
1018
1019 def _priority(self, extension):
1020 renderer_priority = (
1021 0 if extension in self._renderer_extensions else 1)
1022 extension_priority = self.default_priorities.get(
1023 extension, self.FALLBACK_PRIORITY)
1024 return (renderer_priority, extension_priority)
1025
1026 def _sort_according_to_priority(self, matches):
1027
1028 def priority_and_path(match):
1029 return (match.priority, match.path)
1030
1031 return sorted(matches, key=priority_and_path)
1032
1033 def _sort_paths_according_to_priority(self, paths):
1034
1035 def priority_and_path(path):
1036 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1037
1038 return sorted(paths, key=priority_and_path)
1039
1040 def search_old(self, commit):
954 """
1041 """
955 Try to find a readme in the given `commit`.
1042 Try to find a readme in the given `commit`.
956 """
1043 """
@@ -966,3 +1053,18 b' class ReadmeFinder:'
966 continue
1053 continue
967
1054
968 return f
1055 return f
1056
1057
1058 class ReadmeMatch:
1059
1060 def __init__(self, node, match, priority):
1061 self._node = node
1062 self._match = match
1063 self.priority = priority
1064
1065 @property
1066 def path(self):
1067 return self._node.path
1068
1069 def __repr__(self):
1070 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
General Comments 0
You need to be logged in to leave comments. Login now