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

Fix apy modules tests - Re: #516 #522

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
63 changes: 36 additions & 27 deletions modules/apy.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from modules import more
import operator
from humanize import naturaldelta
from enum import Enum

headers = [(
'User-Agent', 'Mozilla/5.0' +
Expand All @@ -22,6 +23,12 @@
Apy_errorData = 'Sorry, Apertium APy did not return any data.'
langRE = r'[a-z]{2,3}(?:_[A-Za-z]+)?'

class Status(Enum):
OK = 0
KEYWORD_ERROR = 1
ENUM_ERROR = 2
SYNTAX_ERROR = 3
WEB_RESPONSE_ERROR = 4

def strict_check(pattern, string, function):
error = ''
Expand All @@ -36,7 +43,6 @@ def strict_check(pattern, string, function):

return string, error


def handle_error(error):
response = error.read()
err = json.loads(response.decode('utf-8'))
Expand Down Expand Up @@ -80,16 +86,17 @@ def translate(phenny, translate_me, input_lang, output_lang='en'):
def apertium_translate(phenny, input):
'''Translates a phrase using APy.'''
pairRE = langRE + r'-' + langRE
line, line_error = strict_check(r'((?:' + pairRE + r'(?:\|' + pairRE + r')*' + r' ?)+)\s+(.*)',
line, syntax_error_msg = strict_check(r'((?:' + pairRE + r'(?:\|' + pairRE + r')*' + r' ?)+)\s+(.*)',
input.group(1), apertium_translate)

if line_error:
phenny.say(line_error)
return
if syntax_error_msg:
phenny.say(syntax_error_msg)
return Status.SYNTAX_ERROR

if (len(line.group(2)) > 350) and (not input.admin):
#raise GrumbleError('Phrase must be under 350 characters.')
phenny.say('Phrase must be under 350 characters.')
return Status.SYNTAX_ERROR

blocks = line.group(1).split(' ')
for block in blocks:
Expand All @@ -99,13 +106,14 @@ def apertium_translate(phenny, input):
if input_lang == output_lang:
#raise GrumbleError('Stop trying to confuse me! Pick different languages ;)')
phenny.say('Stop trying to confuse me! Pick different languages ;)')
return Status.ENUM_ERROR

# TODO: Remove this try/except block? web.decode doesn't seem to raise any GrumbleError's
try:
translated = web.decode(translate(phenny, translated, input_lang, output_lang))
except GrumbleError as err:
phenny.say('{:s}-{:s}: {:s}'.format(input_lang, output_lang, str(err)))
return
return Status.WEB_RESPONSE_ERROR
phenny.reply(web.decode(translated))

apertium_translate.name = 't'
Expand Down Expand Up @@ -179,11 +187,11 @@ def apertium_listpairs(phenny, input):

def apertium_analyse(phenny, input):
'''Analyse text using Apertium APy'''
cmd, cmd_error = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_analyse)
cmd, syntax_error_msg = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_analyse)

if cmd_error:
phenny.say(cmd_error)
return
if syntax_error_msg:
phenny.say(syntax_error_msg)
return Status.SYNTAX_ERROR

opener = urllib.request.build_opener()
opener.addheaders = headers
Expand All @@ -210,11 +218,11 @@ def apertium_analyse(phenny, input):

def apertium_generate(phenny, input):
'''Use Apertium APy's generate functionality'''
cmd, cmd_error = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_generate)
cmd, syntax_error_msg = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_generate)

if cmd_error:
phenny.say(cmd_error)
return
if syntax_error_msg:
phenny.say(syntax_error_msg)
return Status.SYNTAX_ERROR

opener = urllib.request.build_opener()
opener.addheaders = headers
Expand All @@ -241,12 +249,12 @@ def apertium_generate(phenny, input):

def apertium_identlang(phenny, input):
'''Identify the language for a given input.'''
text, text_error = strict_check(r'.*', input.group(1), apertium_identlang)
text, syntax_error_message = strict_check(r'.*', input.group(1), apertium_identlang)
text = text.group(0)

if text_error:
phenny.say(text_error)
return
if syntax_error_message:
phenny.say(syntax_error_message)
return Status.SYNTAX_ERROR

opener = urllib.request.build_opener()
opener.addheaders = headers
Expand Down Expand Up @@ -336,11 +344,11 @@ def plural(num, word, be=False):

def apertium_calccoverage(phenny, input):
'''Calculate translation coverage for a language and a given input.'''
cmd, cmd_error = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_calccoverage)
cmd, syntax_error_msg = strict_check(r'(' + langRE + r')\s+(.*)', input.group(1), apertium_calccoverage)

if cmd_error:
phenny.say(cmd_error)
return
if syntax_error_msg:
phenny.say(syntax_error_msg)
return Status.SYNTAX_ERROR

opener = urllib.request.build_opener()
opener.addheaders = headers
Expand All @@ -362,17 +370,18 @@ def apertium_calccoverage(phenny, input):

def apertium_perword(phenny, input):
'''Perform APy's tagger, morph, translate, and biltrans functions on individual words.'''
cmd, cmd_error = strict_check(r'(' + langRE + r')\s+\((.*)\)\s+(.*)', input.group(1), apertium_perword)
cmd, syntax_error_msg = strict_check(r'(' + langRE + r')\s+\((.*)\)\s+(.*)', input.group(1), apertium_perword)
valid_funcs = {'tagger', 'disambig', 'biltrans', 'translate', 'morph'}

if cmd_error:
phenny.say(cmd_error)
return
if syntax_error_msg:
phenny.say(syntax_error_msg)
return Status.SYNTAX_ERROR

# validate requested functions
funcs = cmd.group(2).split(' ')
if not set(funcs) <= valid_funcs:
raise GrumbleError('The requested functions must be from the set {:s}.'.format(str(valid_funcs)))
phenny.say('The requested functions must be from the set {:s}.'.format(str(valid_funcs)))
return Status.KEYWORD_ERROR

opener = urllib.request.build_opener()
opener.addheaders = headers
Expand Down
37 changes: 20 additions & 17 deletions modules/test/test_apy.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ def reset_mocks(self, *mocks):
for moc in mocks:
moc.reset_mock()

def check_exceptions(self, bad_list, name, reason=None):
def check_error(self, bad_list, name, error, reason=None):
if not reason:
reason = 'invalid input to {:s}'.format(name.__name__)
if not isinstance(error, apy.Status):
raise ValueError('Parameter error must be a Status.')
for inp in bad_list:
self.input.group.return_value = inp
with self.assertRaises(GrumbleError,
msg='No exception raised for {:s}!'.format(reason)):
name.__call__(self.phenny, self.input)
function_return_val = name.__call__(self.phenny, self.input)
self.assertEqual(function_return_val, error, msg='No {:s} returned for {:s}!'.format(error.name, reason))

def test_translate_langs(self, mock_open):
# single language
Expand Down Expand Up @@ -89,10 +90,10 @@ def test_translate_non_langs(self, mock_handle, mock_open):
self.reset_mocks(self.phenny, mock_open, mock_handle)

# bad input
self.check_exceptions([self.texts['spa'], 'spa ' + self.texts['spa'], 'spa-eng'],
apy.apertium_translate)
self.check_exceptions(['en-en Translate to the same language?'],
apy.apertium_translate, 'self-translation')
self.check_error([self.texts['spa'], 'spa ' + self.texts['spa'], 'spa-eng'],
apy.apertium_translate, apy.Status.SYNTAX_ERROR)
self.check_error(['en-en Translate to the same language?'],
apy.apertium_translate, apy.Status.ENUM_ERROR, 'self-translation')
self.reset_mocks(self.phenny, mock_open, mock_handle)

# non-existent language with actual language
Expand All @@ -111,8 +112,9 @@ def test_translate_admin(self, mock_open):

# restricted length for non-admin
self.input.admin = False
self.check_exceptions(['eng-spa ' + self.texts['eng_long']],
apy.apertium_translate, 'long non-admin translation!')
self.check_error(['eng-spa ' + self.texts['eng_long']],
apy.apertium_translate, apy.Status.SYNTAX_ERROR,
'long non-admin translation!')
self.reset_mocks(self.phenny, mock_open)

# non-restricted length for admin
Expand Down Expand Up @@ -189,8 +191,8 @@ def test_analyze_generate(self, mock_addmsgs, mock_open):
self.reset_mocks(mock_open, mock_addmsgs)

# bad input
self.check_exceptions([' '.join(words), 'eng'], apy.apertium_analyse)
self.check_exceptions([' '.join(words), 'eng'], apy.apertium_generate)
self.check_error([' '.join(words), 'eng'], apy.apertium_analyse, apy.Status.SYNTAX_ERROR)
self.check_error([' '.join(words), 'eng'], apy.apertium_generate, apy.Status.SYNTAX_ERROR)

@mock.patch('modules.apy.more.add_messages')
def test_identlang(self, mock_addmsgs, mock_open):
Expand Down Expand Up @@ -224,7 +226,7 @@ def test_coverage(self, mock_open):
self.reset_mocks(self.phenny, mock_open)

# bad input
self.check_exceptions(['eng', self.texts['eng'], 'eng-spa'], apy.apertium_calccoverage)
self.check_error(['eng', self.texts['eng'], 'eng-spa'], apy.apertium_calccoverage, apy.Status.SYNTAX_ERROR)

def test_perword(self, mock_open):
# valid perword functions
Expand All @@ -248,7 +250,8 @@ def test_perword(self, mock_open):
self.reset_mocks(self.phenny, mock_open)

# bad input
self.check_exceptions(['fra (tagger nonfunc) word'], apy.apertium_perword,
'invalid perword function')
self.check_exceptions(['fra', 'fra (tagger)', '(tagger)', 'fra word',
'(tagger morph) word'], apy.apertium_perword)
self.check_error(['fra (tagger nonfunc) word'], apy.apertium_perword,
apy.Status.KEYWORD_ERROR, 'invalid perword function')
self.check_error(['fra', 'fra (tagger)', '(tagger)', 'fra word',
'(tagger morph) word'], apy.apertium_perword,
apy.Status.SYNTAX_ERROR)