Skip to content

Commit a79bca7

Browse files
committed
unit tests added
1 parent 2b44b11 commit a79bca7

File tree

8 files changed

+86
-39
lines changed

8 files changed

+86
-39
lines changed

src/autocomplete/levenshteinTrie.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ class LevenshteinTrie:
2121
def __init__(self):
2222
self._root: TrieNode = TrieNode(letter="", wordpath="")
2323

24-
def insert(self, word: str, owner: object) -> None:
25-
self._root.insert(word.lower(), owner)
24+
def insert(self, word: str, owner: object=None) -> None:
25+
self._root.insert(word.lower(), word, owner)
2626

2727
# The search function returns a list of all words that are less than the
2828
# given maximum distance from the target word
29-
def search(self, word: str, maxCost: int) -> List[NodeMatch]:
29+
def search(self, word: str, maxCost: int) -> List[ResultMatch]:
3030

3131
cumulativeResult: List[NodeMatch] = []
3232
tracker = LevenshteinTracker(word.lower(), maxCost)
@@ -35,7 +35,7 @@ def search(self, word: str, maxCost: int) -> List[NodeMatch]:
3535
self._searchBranch(
3636
self._root.children[char], cumulativeResult, tracker)
3737

38-
return list(map(lambda n: ResultMatch(n.node.wordpath, n.node.owner, n.cost), cumulativeResult))
38+
return list(map(lambda n: ResultMatch(n.node.finalword, n.node.owner, n.cost), cumulativeResult))
3939

4040
def _searchBranch(self, node: TrieNode, cumulativeResult: List[NodeMatch], tracker: LevenshteinTracker):
4141

src/autocomplete/resultMatch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ def __init__(self, word: str, owner: object, cost: int):
77
self.cost: int = cost
88

99
def __repr__(self):
10-
return "'{}' -> {} ({})".format(self.word, self.owner, self.cost)
10+
return "'{}' - {}".format(self.word, self.cost)

src/autocomplete/trieNode.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ class TrieNode:
55
def __init__(self, letter: str, wordpath: str):
66
self.letter: str = letter
77
self.wordpath: str = wordpath
8+
self.finalword: str = None
89
self.owner: object = None
910
self.children: Dict[str, TrieNode] = {}
1011

11-
def insert(self, wordpath: str, owner: object) -> None:
12-
leaf: TrieNode = self._createOrGetLeaf(wordpath)
13-
leaf.owner = owner
12+
def insert(self, wordparsed: str, finalword: str, owner: object=None) -> None:
13+
leaf: TrieNode = self._createOrGetLeaf(wordparsed)
14+
leaf.finalword = finalword
15+
leaf.owner = owner if owner is not None else finalword
1416

1517
def isWholeWord(self) -> bool:
1618
return self.owner is not None

src/hello/search/placeSearchEngine.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ def search(self, query) -> PlaceSearchResult:
2222
if query:
2323
queryParsed: str = query.strip()
2424

25-
if queryParsed:
25+
start = time.time()
2626

27-
start = time.time()
27+
if queryParsed:
2828
places = self._searchStrategy.search(queryParsed)
29-
end = time.time()
3029
places = places[0:self._config.maxNumberResults]
3130

31+
end = time.time()
32+
3233
return PlaceSearchResult(places, start, end)

src/src.pyproj

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
<Compile Include="hello\search\placeSearchEngine.py">
5757
<SubType>Code</SubType>
5858
</Compile>
59-
<Compile Include="hello\search\placeSearchQueryStrategyNew.py" />
6059
<Compile Include="hello\search\placeSearchQueryStrategy.py">
6160
<SubType>Code</SubType>
6261
</Compile>
@@ -66,7 +65,6 @@
6665
<Compile Include="hello\search\placeSearchResult.py">
6766
<SubType>Code</SubType>
6867
</Compile>
69-
<Compile Include="hello\search\trienew.py" />
7068
<Compile Include="hello\search\trie.py">
7169
<SubType>Code</SubType>
7270
</Compile>
@@ -90,19 +88,12 @@
9088
</ItemGroup>
9189
<ItemGroup>
9290
<Folder Include="autocomplete\" />
93-
<Folder Include="autocomplete\__pycache__\" />
9491
<Folder Include="hello\" />
9592
<Folder Include="hello\endpoints\" />
9693
<Folder Include="hello\search\" />
9794
</ItemGroup>
9895
<ItemGroup>
9996
<Content Include=".pylintrc" />
100-
<Content Include="autocomplete\__pycache__\levenshteinTracker.cpython-36.pyc" />
101-
<Content Include="autocomplete\__pycache__\levenshteinTrie.cpython-36.pyc" />
102-
<Content Include="autocomplete\__pycache__\resultMatch.cpython-36.pyc" />
103-
<Content Include="autocomplete\__pycache__\trienew.cpython-36.pyc" />
104-
<Content Include="autocomplete\__pycache__\trieNode.cpython-36.pyc" />
105-
<Content Include="autocomplete\__pycache__\__init__.cpython-36.pyc" />
10697
<Content Include="requirements.txt" />
10798
</ItemGroup>
10899
<ItemGroup>

test/test.pyproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@
2929
</PropertyGroup>
3030
<ItemGroup>
3131
<Compile Include="runserver.py" />
32+
<Compile Include="test\autocomplete\test_levenshteinTrie.py">
33+
<SubType>Code</SubType>
34+
</Compile>
3235
<Compile Include="test\search\test_placeSearchEngine.py">
3336
<SubType>Code</SubType>
3437
</Compile>
3538
<Compile Include="test\__init__.py" />
3639
</ItemGroup>
3740
<ItemGroup>
3841
<Folder Include="test\" />
42+
<Folder Include="test\autocomplete\" />
3943
<Folder Include="test\search\" />
4044
</ItemGroup>
4145
<ItemGroup>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import unittest
2+
from typing import Tuple, List
3+
from unittest.mock import MagicMock
4+
from autocomplete.levenshteinTrie import LevenshteinTrie
5+
from autocomplete.resultMatch import ResultMatch
6+
7+
class Test_LevenshteinTrie(unittest.TestCase):
8+
9+
def test_emptytree_emptyresult(self):
10+
11+
self.search_DataDrivenTest(
12+
database=(),
13+
query="any", maxCost=3,
14+
expectedResult=[])
15+
16+
def test_search_partialWord_approximatedWordsReturned(self):
17+
18+
self.search_DataDrivenTest(
19+
database=("Batman", "Robin", "Batman & Robin", "Wonder Woman", "Robin Hood", "Batgirl & Robin"),
20+
query="bat man", maxCost=3,
21+
expectedResult=["Batman", "Batman & Robin"])
22+
23+
def test_search_similarWord_approximatedWordsReturned(self):
24+
25+
self.search_DataDrivenTest(
26+
database=("Batman", "Robin", "Batman & Robin", "Wonder Woman", "Robin Hood", "Batgirl & Robin"),
27+
query="bat man & rboin", maxCost=3,
28+
expectedResult=["Batman & Robin"])
29+
30+
def search_DataDrivenTest(self, database: Tuple[str,...], query:str, maxCost: int, expectedResult: Tuple[str,...]) -> List[ResultMatch]:
31+
32+
#arrange
33+
trie: LevenshteinTrie = self.createTrie(database)
34+
35+
#act
36+
result: List[ResultMatch] = trie.search(query, maxCost)
37+
38+
#assert
39+
assert len(result) == len(expectedResult)
40+
assert all(r.word in expectedResult for r in result)
41+
42+
def createTrie(self, words: Tuple[str, ...]) -> LevenshteinTrie:
43+
44+
trie = LevenshteinTrie()
45+
46+
for word in words:
47+
trie.insert(word)
48+
49+
return trie

test/test/search/test_placeSearchEngine.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,64 +4,64 @@
44

55
from hello.search.placeSearchEngine import PlaceSearchEngine
66
from hello.search.placeSearchConfig import PlaceSearchConfig
7-
from hello.search.placeSearchQueryStrategy import IPlaceSearchStrategy
8-
from hello.place import Place
7+
from hello.search.placeSearchResult import PlaceSearchResult
8+
from hello.core import IPlaceSearchQueryStrategy, PlaceScore
99

1010
class Test_placeSearchEngine(unittest.TestCase):
1111

1212
def test_search_emptyStrategyResult_emptyCollection(self):
1313
#arrange
14-
query = 'auto'
15-
strategy: IPlaceSearchStrategy = self.createStrategy(result=())
14+
query:str = 'auto'
15+
strategy: IPlaceSearchQueryStrategy = self.createStrategy(result=())
1616
config = PlaceSearchConfig(maxNumberResults=10)
1717
engine = PlaceSearchEngine(strategy, config)
1818

1919
#act
20-
result: Tuple[Place] = engine.search(query)
20+
result: PlaceSearchResult = engine.search(query)
2121

2222
#assert
23-
assert len(result) == 0
23+
assert len(result.places) == 0
2424
assert strategy.search.called
2525

2626
def test_search_emptyQuery_emptyCollection(self):
2727
#arrange
28-
query = ''
29-
strategy: IPlaceSearchStrategy = self.createStrategy(result=())
28+
query:str = ''
29+
strategy: IPlaceSearchQueryStrategy = self.createStrategy(result=())
3030
config = PlaceSearchConfig(maxNumberResults=10)
3131
engine = PlaceSearchEngine(strategy, config)
3232

3333
#act
34-
result: Tuple[Place] = engine.search(query)
34+
result: PlaceSearchResult = engine.search(query)
3535

3636
#assert
37-
self.assertEqual(len(result), 0)
37+
assert len(result.places) == 0
3838
assert not strategy.search.called
3939

4040
def test_search_invalidConfig_error(self):
4141
#arrange
42-
query = 'auto'
43-
strategy: IPlaceSearchStrategy = self.createStrategy(result=())
42+
query:str = 'auto'
43+
strategy: IPlaceSearchQueryStrategy = self.createStrategy(result=())
4444
config = PlaceSearchConfig(maxNumberResults=0)
4545
engine = PlaceSearchEngine(strategy, config)
4646

4747
#act / assert
4848
self.assertRaises(ValueError, engine.search, query)
4949

50-
def test_search_queryCaseEmptySpaces_queryParsed(self):
50+
def test_search_queryTrailingSpaces_queryParsed(self):
5151
#arrange
52-
query = ' Auto Complete '
53-
strategy: IPlaceSearchStrategy = self.createStrategy(result=())
52+
query:str = ' Auto Complete '
53+
strategy: IPlaceSearchQueryStrategy = self.createStrategy(result=())
5454
config = PlaceSearchConfig(maxNumberResults=2)
5555
engine = PlaceSearchEngine(strategy, config)
5656

5757
#act
58-
result: Tuple[Place] = engine.search(query)
58+
result: PlaceSearchResult = engine.search(query)
5959

6060
#assert
61-
strategy.search.assert_called_with('auto complete')
61+
strategy.search.assert_called_with('Auto Complete')
6262

63-
def createStrategy(self, result: Tuple[Place]) -> IPlaceSearchStrategy:
64-
strategy = IPlaceSearchStrategy()
63+
def createStrategy(self, result: Tuple[PlaceScore]) -> IPlaceSearchQueryStrategy:
64+
strategy = IPlaceSearchQueryStrategy()
6565
strategy.search = MagicMock(return_value=result)
6666
return strategy
6767

0 commit comments

Comments
 (0)