Commit 762943b5 by Peter Baratta

Change tests to use `latex_preview` rather than internal methods.

Rather than test internal methods, like `render_atom` and the such, instead
compare the expected output of the externally-visible `latex_preview` and
test preview.py as a whole.
parent 2df0f02c
...@@ -8,7 +8,7 @@ Because intermediate values of the render contain more data than simply the ...@@ -8,7 +8,7 @@ Because intermediate values of the render contain more data than simply the
string of latex, store it in a custom class `LatexRendered`. string of latex, store it in a custom class `LatexRendered`.
""" """
from calc import ParseAugmenter, add_defaults, SUFFIXES from calc import ParseAugmenter, DEFAULT_VARIABLES, DEFAULT_FUNCTIONS, SUFFIXES
class LatexRendered(object): class LatexRendered(object):
...@@ -209,6 +209,9 @@ def render_parallel(children): ...@@ -209,6 +209,9 @@ def render_parallel(children):
""" """
Simply join the child nodes with a double vertical line. Simply join the child nodes with a double vertical line.
""" """
if len(children) == 1:
return children[0]
children_latex = [k.latex for k in children if k.latex != "||"] children_latex = [k.latex for k in children if k.latex != "||"]
latex = r"\|".join(children_latex) latex = r"\|".join(children_latex)
tall = any(k.tall for k in children) tall = any(k.tall for k in children)
...@@ -249,6 +252,9 @@ def render_product(children): ...@@ -249,6 +252,9 @@ def render_product(children):
'a*b/c/d' -> '\frac{a\cdot b}{c\cdot d}' 'a*b/c/d' -> '\frac{a\cdot b}{c\cdot d}'
'a/b*c/d*e' -> '\frac{a}{b}\cdot \frac{c}{d}\cdot e' 'a/b*c/d*e' -> '\frac{a}{b}\cdot \frac{c}{d}\cdot e'
""" """
if len(children) == 1:
return children[0]
position = "numerator" # or denominator position = "numerator" # or denominator
fraction_mode_ever = False fraction_mode_ever = False
numerator = [] numerator = []
...@@ -296,6 +302,9 @@ def render_sum(children): ...@@ -296,6 +302,9 @@ def render_sum(children):
""" """
Concatenate elements, including the operators. Concatenate elements, including the operators.
""" """
if len(children) == 1:
return children[0]
children_latex = [k.latex for k in children] children_latex = [k.latex for k in children]
latex = "".join(children_latex) latex = "".join(children_latex)
tall = any(k.tall for k in children) tall = any(k.tall for k in children)
...@@ -316,6 +325,25 @@ def render_atom(children): ...@@ -316,6 +325,25 @@ def render_atom(children):
return children[0] return children[0]
def add_defaults(var, fun, case_sensitive=False):
"""
Create sets with both the default and user-defined variables.
Compare to calc.add_defaults
"""
var_items = set(DEFAULT_VARIABLES)
fun_items = set(DEFAULT_FUNCTIONS)
var_items.update(var)
fun_items.update(fun)
if not case_sensitive:
var_items = set(k.lower() for k in var_items)
fun_items = set(k.lower() for k in fun_items)
return var_items, fun_items
def latex_preview(math_expr, variables=(), functions=(), case_sensitive=False): def latex_preview(math_expr, variables=(), functions=(), case_sensitive=False):
""" """
Convert `math_expr` into latex, guaranteeing its parse-ability. Convert `math_expr` into latex, guaranteeing its parse-ability.
...@@ -341,8 +369,8 @@ def latex_preview(math_expr, variables=(), functions=(), case_sensitive=False): ...@@ -341,8 +369,8 @@ def latex_preview(math_expr, variables=(), functions=(), case_sensitive=False):
render_actions = { render_actions = {
'number': render_number, 'number': render_number,
'variable': variable_closure(set(variables), casify), 'variable': variable_closure(variables, casify),
'function': function_closure(set(functions), casify), 'function': function_closure(functions, casify),
'atom': render_atom, 'atom': render_atom,
'power': render_power, 'power': render_power,
'parallel': render_parallel, 'parallel': render_parallel,
......
...@@ -8,22 +8,7 @@ import preview ...@@ -8,22 +8,7 @@ import preview
import pyparsing import pyparsing
class PreviewTestUtility(unittest.TestCase): class LatexRenderedTest(unittest.TestCase):
"""
Provide utilities for the preview test cases.
"""
def assert_latex_rendered(self, to_be_tested, tall=False, latex=None, sans_parens=None):
"""
Compare a `LatexRendered` object against the data it should store.
"""
if sans_parens is None:
sans_parens = latex
self.assertEquals(to_be_tested.sans_parens, sans_parens)
self.assertEquals(to_be_tested.latex, latex)
self.assertEquals(to_be_tested.tall, tall)
class LatexRenderedTest(PreviewTestUtility):
""" """
Test the initializing code for LatexRendered. Test the initializing code for LatexRendered.
...@@ -35,19 +20,18 @@ class LatexRenderedTest(PreviewTestUtility): ...@@ -35,19 +20,18 @@ class LatexRenderedTest(PreviewTestUtility):
""" """
math = 'x^2' math = 'x^2'
obj = preview.LatexRendered(math, tall=True) obj = preview.LatexRendered(math, tall=True)
self.assert_latex_rendered(obj, tall=True, latex=math) self.assertEquals(obj.latex, math)
self.assertEquals(obj.sans_parens, math)
self.assertEquals(obj.tall, True)
def _each_parens(self, with_parens, math, parens, tall=False): def _each_parens(self, with_parens, math, parens, tall=False):
""" """
Helper method to test the way parens are wrapped. Helper method to test the way parens are wrapped.
""" """
obj = preview.LatexRendered(math, parens=parens, tall=tall) obj = preview.LatexRendered(math, parens=parens, tall=tall)
self.assert_latex_rendered( self.assertEquals(obj.latex, with_parens)
obj, self.assertEquals(obj.sans_parens, math)
tall=tall, self.assertEquals(obj.tall, tall)
latex=with_parens,
sans_parens=math
)
def test_parens(self): def test_parens(self):
""" Test curvy parens. """ """ Test curvy parens. """
...@@ -79,213 +63,146 @@ class LatexRenderedTest(PreviewTestUtility): ...@@ -79,213 +63,146 @@ class LatexRenderedTest(PreviewTestUtility):
preview.LatexRendered('x^2', parens='not parens') preview.LatexRendered('x^2', parens='not parens')
def _latex_rendered_list(string_children): class LatexPreviewTest(unittest.TestCase):
"""
Helper method to wrap `LatexRendered` objects around each child.
""" """
return [preview.LatexRendered(k) for k in string_children] Run integrative tests for `latex_preview`.
class RenderMethodsTest(PreviewTestUtility): All functionality was tested `RenderMethodsTest`, but see if it combines
all together correctly.
""" """
Test each of the `render_X` methods. def test_no_input(self):
"""
With no input (including just whitespace), see that no error is thrown.
"""
self.assertEquals('', preview.latex_preview(''))
self.assertEquals('', preview.latex_preview(' '))
self.assertEquals('', preview.latex_preview(' \t '))
They take a list of `LatexRendered`s and, depending on the type of node,
combine them all together into one `LatexRendered`.
"""
def test_number_simple(self): def test_number_simple(self):
""" Simple numbers should pass through `render_number`. """ """ Simple numbers should pass through. """
kids = [preview.LatexRendered('3.1415')] self.assertEquals(preview.latex_preview('3.1415'), '3.1415')
out = preview.render_number(kids)
self.assert_latex_rendered(out, latex='3.1415')
def test_number_suffix(self): def test_number_suffix(self):
""" Suffixes should be escaped in `render_number`. """ """ Suffixes should be escaped. """
kids = _latex_rendered_list(['1.618', 'k']) self.assertEquals(preview.latex_preview('1.618k'), r'1.618\text{k}')
out = preview.render_number(kids)
self.assert_latex_rendered(out, latex=r'1.618\text{k}')
def test_number_sci_notation(self): def test_number_sci_notation(self):
""" Numbers with scientific notation should display nicely """ """ Numbers with scientific notation should display nicely """
kids = _latex_rendered_list(['6.0221413', 'E', '+', '23']) self.assertEquals(
out = preview.render_number(kids) preview.latex_preview('6.0221413E+23'),
self.assert_latex_rendered( r'6.0221413\!\times\!10^{+23}'
out,
latex=r'6.0221413\!\times\!10^{+23}', tall=True
) )
self.assertEquals(
kids = _latex_rendered_list(['-', '6.0221413', 'E', '+', '23']) preview.latex_preview('-6.0221413E+23'),
out = preview.render_number(kids) r'-6.0221413\!\times\!10^{+23}'
self.assert_latex_rendered(
out,
latex=r'-6.0221413\!\times\!10^{+23}', tall=True
) )
def test_number_sci_notation_suffix(self): def test_number_sci_notation_suffix(self):
""" Test numbers with both """ """ Test numbers with both of these. """
kids = _latex_rendered_list(['6.0221413', 'E', '+', '23', 'k']) self.assertEquals(
out = preview.render_number(kids) preview.latex_preview('6.0221413E+23k'),
self.assert_latex_rendered( r'6.0221413\!\times\!10^{+23}\text{k}'
out, )
latex=r'6.0221413\!\times\!10^{+23}\text{k}', tall=True self.assertEquals(
preview.latex_preview('-6.0221413E+23k'),
r'-6.0221413\!\times\!10^{+23}\text{k}'
) )
def test_variable_simple(self): def test_variable_simple(self):
""" Simple valid variables should pass through `render_variable`. """ """ Simple valid variables should pass through. """
render_variable = preview.variable_closure(['x', 'y'], lambda x: x.lower()) self.assertEquals(preview.latex_preview('x', variables=['x']), 'x')
kids = [preview.LatexRendered('x')]
out = render_variable(kids)
self.assert_latex_rendered(out, latex='x')
def test_variable_unicode(self):
"""
Simple valid unicode variables should pass through as well.
Note: it isn't supported right now in the rest of the code (esp. the
way we have set up pyparsing), but when it is, this should work.
"""
var = u"✖"
render_variable = preview.variable_closure([var], lambda x: x.lower())
out = render_variable([preview.LatexRendered(var)])
self.assert_latex_rendered(out, latex=var)
def test_greek(self): def test_greek(self):
""" Variable names that are greek should be formatted accordingly. """ """ Variable names that are greek should be formatted accordingly. """
render_variable = preview.variable_closure(['pi'], lambda x: x.lower()) self.assertEquals(preview.latex_preview('pi'), r'\pi')
out = render_variable([preview.LatexRendered('pi')])
self.assert_latex_rendered(out, latex=r'\pi')
def test_variable_subscript(self): def test_variable_subscript(self):
""" Things like 'epsilon_max' should display nicely """ """ Things like 'epsilon_max' should display nicely """
render_variable = preview.variable_closure(['epsilon_max'], lambda x: x.lower()) self.assertEquals(
out = render_variable([preview.LatexRendered('epsilon_max')]) preview.latex_preview('epsilon_max', variables=['epsilon_max']),
self.assert_latex_rendered(out, latex=r'\epsilon_{max}') r'\epsilon_{max}'
)
def test_function_simple(self): def test_function_simple(self):
""" Valid function names should be escaped in `render_function`. """ """ Valid function names should be escaped. """
render_function = preview.function_closure(['f'], lambda x: x.lower()) self.assertEquals(
kids = _latex_rendered_list(['f', 'x']) preview.latex_preview('f(3)', functions=['f']),
out = render_function(kids) r'\text{f}(3)'
self.assert_latex_rendered(out, latex=r'\text{f}(x)') )
def test_function_tall(self): def test_function_tall(self):
r""" Functions surrounding a tall element should have \left, \right """ r""" Functions surrounding a tall element should have \left, \right """
render_function = preview.function_closure(['f'], lambda x: x.lower()) self.assertEquals(
out = render_function([ preview.latex_preview('f(3^2)', functions=['f']),
preview.LatexRendered('f'), r'\text{f}\left(3^{2}\right)'
preview.LatexRendered('x^2', tall=True) )
])
self.assert_latex_rendered(out, latex=r'\text{f}\left(x^2\right)', tall=True)
def test_function_sqrt(self): def test_function_sqrt(self):
""" Sqrt function should be handled specially. """ """ Sqrt function should be handled specially. """
render_function = preview.function_closure(['sqrt'], lambda x: x.lower()) self.assertEquals(preview.latex_preview('sqrt(3)'), r'\sqrt{3}')
kids = _latex_rendered_list(['sqrt', 'x'])
out = render_function(kids)
self.assert_latex_rendered(out, latex=r'\sqrt{x}')
def test_function_log10(self): def test_function_log10(self):
""" log10 function should be handled specially. """ """ log10 function should be handled specially. """
render_function = preview.function_closure(['log10'], lambda x: x.lower()) self.assertEquals(preview.latex_preview('log10(3)'), r'\log_{10}(3)')
kids = _latex_rendered_list(['log10', 'x'])
out = render_function(kids)
self.assert_latex_rendered(out, latex=r'\log_{10}(x)')
def test_function_log2(self): def test_function_log2(self):
""" log2 function should be handled specially. """ """ log2 function should be handled specially. """
render_function = preview.function_closure(['log2'], lambda x: x.lower()) self.assertEquals(preview.latex_preview('log2(3)'), r'\log_2(3)')
kids = _latex_rendered_list(['log2', 'x'])
out = render_function(kids)
self.assert_latex_rendered(out, latex=r'\log_2(x)')
def test_power_simple(self): def test_power_simple(self):
""" `render_power` should wrap the elements with braces correctly. """ """ Powers should wrap the elements with braces correctly. """
kids = _latex_rendered_list(['x', '^', 'y', '^', '2']) self.assertEquals(preview.latex_preview('2^3^4'), '2^{3^{4}}')
out = preview.render_power(kids)
self.assert_latex_rendered(out, latex='x^{y^{2}}', tall=True)
def test_power_parens(self): def test_power_parens(self):
""" `render_power` should ignore the parenthesis of the last math. """ """ Powers should ignore the parenthesis of the last math. """
kids = _latex_rendered_list(['x', '^', 'y', '^']) # (x+y) self.assertEquals(preview.latex_preview('2^3^(4+5)'), '2^{3^{4+5}}')
kids.append(preview.LatexRendered('x+y', parens='('))
out = preview.render_power(kids)
self.assert_latex_rendered(out, latex='x^{y^{x+y}}', tall=True)
def test_parallel(self): def test_parallel(self):
r""" `render_power` should combine its elements with '\|'. """ r""" Parallel items should combine with '\|'. """
kids = _latex_rendered_list(['x', '||', 'y']) self.assertEquals(preview.latex_preview('2||3'), r'2\|3')
out = preview.render_parallel(kids)
self.assert_latex_rendered(out, latex=r'x\|y')
def test_product_mult_only(self): def test_product_mult_only(self):
r""" `render_product` should combine a product with a '\cdot'. """ r""" Simple products should combine with a '\cdot'. """
kids = _latex_rendered_list(['x', '*', 'y']) self.assertEquals(preview.latex_preview('2*3'), r'2\cdot 3')
out = preview.render_product(kids)
self.assert_latex_rendered(out, latex=r'x\cdot y')
def test_product_big_frac(self): def test_product_big_frac(self):
""" `render_product` should combine a fraction with '\frac'. """ """ Division should combine with '\frac'. """
kids = _latex_rendered_list(list('w*x/y/z')) self.assertEquals(
out = preview.render_product(kids) preview.latex_preview('2*3/4/5'),
self.assert_latex_rendered(out, latex=r'\frac{w\cdot x}{y\cdot z}', tall=True) r'\frac{2\cdot 3}{4\cdot 5}'
)
def test_product_single_frac(self): def test_product_single_frac(self):
""" `render_product` should ignore parens if they are extraneous. """ """ Division should ignore parens if they are extraneous. """
out = preview.render_product([ self.assertEquals(
preview.LatexRendered('x+1', parens='('), preview.latex_preview('(2+3)/(4+5)'),
preview.LatexRendered('/'), r'\frac{2+3}{4+5}'
preview.LatexRendered('y+1', parens='(') )
])
self.assert_latex_rendered(out, latex=r'\frac{x+1}{y+1}', tall=True)
def test_product_keep_going(self): def test_product_keep_going(self):
""" `render_product` should split into many '\frac's when needed. """ """
kids = _latex_rendered_list(list('p/q*r/s*t')) Complex products/quotients should split into many '\frac's when needed.
out = preview.render_product(kids) """
self.assert_latex_rendered(out, latex=r'\frac{p}{q}\cdot \frac{r}{s}\cdot t', tall=True) self.assertEquals(
preview.latex_preview('2/3*4/5*6'),
r'\frac{2}{3}\cdot \frac{4}{5}\cdot 6'
)
def test_sum(self): def test_sum(self):
""" `render_sum` should combine its elements. """ """ Sums should combine its elements. """
kids = _latex_rendered_list(list('-a+b-c+d')) # Use 'x' as the first term (instead of, say, '1'), so it can't be
out = preview.render_sum(kids) # interpreted as a negative number.
self.assert_latex_rendered(out, latex=r'-a+b-c+d') self.assertEquals(
preview.latex_preview('-x+2-3+4', variables=['x']),
'-x+2-3+4'
)
def test_sum_tall(self): def test_sum_tall(self):
""" `render_sum` should pass on `tall`-ness. """ """ A complicated expression should not hide the tallness. """
out = preview.render_sum([ self.assertEquals(
preview.LatexRendered('x'), preview.latex_preview('(2+3^2)'),
preview.LatexRendered('+'), r'\left(2+3^{2}\right)'
preview.LatexRendered('y^2', tall=True) )
])
self.assert_latex_rendered(out, latex='x+y^2', tall=True)
def test_atom_simple(self):
""" `render_atom` should pass through items without parens. """
out = preview.render_atom([preview.LatexRendered('x')])
self.assert_latex_rendered(out, latex='x')
def test_atom_parens(self):
""" Items wrapped in parens should have a `sans_parens` value. """
kids = _latex_rendered_list(list('(x)'))
out = preview.render_atom(kids)
self.assert_latex_rendered(out, latex='(x)', sans_parens='x')
class LatexPreviewTest(unittest.TestCase):
"""
Run integrative tests for `latex_preview`.
All functionality was tested `RenderMethodsTest`, but see if it combines
all together correctly.
"""
def test_no_input(self):
"""
With no input (including just whitespace), see that no error is thrown.
"""
self.assertEquals('', preview.latex_preview(''))
self.assertEquals('', preview.latex_preview(' '))
self.assertEquals('', preview.latex_preview(' \t '))
def test_complicated(self): def test_complicated(self):
""" """
...@@ -297,8 +214,10 @@ class LatexPreviewTest(unittest.TestCase): ...@@ -297,8 +214,10 @@ class LatexPreviewTest(unittest.TestCase):
) )
self.assertEquals( self.assertEquals(
preview.latex_preview('log10(1+3/4/Cos(x^2)*(x+1))', case_sensitive=True), preview.latex_preview('log10(1+3/4/Cos(x^2)*(x+1))',
r'\log_{10}\left(1+\frac{3}{4\cdot \text{Cos}\left(x^{2}\right)}\cdot (x+1)\right)' case_sensitive=True),
(r'\log_{10}\left(1+\frac{3}{4\cdot \text{Cos}\left(x^{2}\right)}'
r'\cdot (x+1)\right)')
) )
def test_syntax_errors(self): def test_syntax_errors(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment