<commit_before># -*- coding: utf-8 -*-
"""
test.py: Testing suite for other modules.
"""
from __future__ import division, unicode_literals
import unittest
import nose
import numpy as np
from atmos import equations
from atmos import util
from atmos import decorators
from nose.tools import raises
from atmos.constants import Rd
from atmos.solve import BaseSolver, FluidSolver, calculate, \
    _get_module_methods, _get_calculatable_methods_dict,\
    _get_shortest_solution
from atmos.util import quantity_string, assumption_list_string, \
    quantity_spec_string, doc_paragraph, \
    strings_to_list_string
try:
    from inspect import getfullargspec
except ImportError:
    from inspect import getargspec as getfullargspec


def test_quantities_dict_complete():
    names = [item['output'] for item in _get_module_methods(equations)]
    for name in names:
        if name not in equations.quantities.keys():
            try:
                util.parse_derivative_string(name)
            except ValueError:
                raise AssertionError('{0} not in quantities dict'.format(name))


def test_get_module_methods_nonempty():
    result = _get_module_methods(equations)
    assert len(result) > 0


def test_default_assumptions_exist():
    for m in FluidSolver.default_assumptions:
        if m not in FluidSolver.all_assumptions:
            raise AssertionError('{0} not a valid assumption'.format(m))


class ddxTests(unittest.TestCase):

    def setUp(self):
        self.data = np.zeros((2, 3))
        self.data[:] = np.array([0., 5., 10.])[None, :]
        self.axis1 = np.array([5., 6., 7.])
        self.axis2 = np.array([[5., 6., 7.], [8., 10., 12.]])
        self.deriv1 = 5.*np.ones((2, 3))
        self.deriv2 = 5.*np.ones((2, 3))
        self.deriv2[1, :] *= 0.5

    @raises(ValueError)
    def test_invalid_data_axis(self):
        util.ddx(self.data, axis=2)

    @raises(ValueError)
    def test_invalid_axis_x(self):
        util.ddx(self.data, axis=2, x=self.axis1, axis_x=1)


class ClosestValTests(unittest.TestCase):

    def setUp(self):
        self.list = [1., 5., 10.]
        self.array = np.array([1., 5., 10.])

    def tearDown(self):
        self.list = None
        self.array = None

    @raises(ValueError)
    def testValueErrorOnEmptyList(self):
        util.closest_val(1., [])

    @raises(ValueError)
    def testValueErrorOnEmptyArray(self):
        util.closest_val(1., np.array([]))

    def testClosestValSingle(self):
        val = util.closest_val(1., np.array([50.]))
        assert val == 0

    def testClosestValInternal(self):
        val = util.closest_val(3.5, self.array)
        assert val == 1

    def testClosestValBelow(self):
        val = util.closest_val(-5., self.array)
        assert val == 0

    def testClosestValAbove(self):
        val = util.closest_val(20., self.array)
        assert val == 2

    def testClosestValInternalNegative(self):
        val = util.closest_val(-3.5, -1*self.array)
        assert val == 1

    def testClosestValInternalList(self):
        val = util.closest_val(3.5, self.list)
        assert val == 1

    def testClosestValBelowList(self):
        val = util.closest_val(-5., self.list)
        assert val == 0

    def testClosestValAboveList(self):
        val = util.closest_val(20., self.list)
        assert val == 2


class AreaPolySphereTests(unittest.TestCase):

    def setUp(self):
        self.lat1 = np.array([0.0, 90.0, 0.0])
        self.lon1 = np.array([0.0, 0.0, 90.0])
        self.area1 = 1.5708
        self.tol1 = 0.0001

    @raises(ValueError)
    def test_area_poly_sphere_insufficient_vertices(self):
        util.area_poly_sphere(self.lat1[:2], self.lon1[:2], 1.)

    def test_area_poly_sphere(self):
        area_calc = util.area_poly_sphere(self.lat1, self.lon1, 1.)
        assert abs(area_calc - self.area1) < self.tol1

    def test_area_poly_sphere_different_radius(self):
        area_calc = util.area_poly_sphere(self.lat1, self.lon1, 2.)
        assert abs(area_calc - 4*self.area1) < 4*self.tol1


class DecoratorTests(unittest.TestCase):

    def setUp(self):
        def dummyfunction(x):
            '''Dummy docstring'''
            return x
        self.func = dummyfunction
        self.func_name = dummyfunction.__name__
        self.func_argspec = getfullargspec(dummyfunction)
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }
        self.assumption_dict = {
            'a1': 'a1_long',
            'a2': 'a2_long',
            'a3': 'a3_long',
        }
        self.func_argspec = getfullargspec(self.func)

    def tearDown(self):
        self.func = None
        self.quantity_dict = None
        self.assumption_dict = None

    def test_assumes_empty(self, **kwargs):
        func = decorators.assumes()(self.func)
        assert func.assumptions == ()
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_assumes_single(self, **kwargs):
        func = decorators.assumes('a1')(self.func)
        assert func.assumptions == ('a1',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_assumes_triple(self, **kwargs):
        func = decorators.assumes('a1', 'a2', 'a3')(self.func)
        assert func.assumptions == ('a1', 'a2', 'a3',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_empty(self, **kwargs):
        func = decorators.overridden_by_assumptions()(self.func)
        assert func.overridden_by_assumptions == ()
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_single(self, **kwargs):
        func = decorators.overridden_by_assumptions('a1')(self.func)
        assert func.overridden_by_assumptions == ('a1',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_triple(self, **kwargs):
        func = decorators.overridden_by_assumptions(
            'a1', 'a2', 'a3')(self.func)
        assert func.overridden_by_assumptions == ('a1', 'a2', 'a3',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    @raises(ValueError)
    def test_autodoc_invalid_no_extra_args(self):
        def invalid_function(T, p):
            return T
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict)(invalid_function)

    @raises(ValueError)
    def test_autodoc_invalid_args_function(self):
        def T_from_x_y(x, y):
            return x
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict)(T_from_x_y)

    @raises(ValueError)
    def test_autodoc_invalid_extra_args(self):
        def invalid_function(T, p):
            return T
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict, equation='x=y',
            references='reference here', notes='c sharp')(invalid_function)


class StringUtilityTests(unittest.TestCase):

    def setUp(self):
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }
        self.assumption_dict = {
            'a1': 'a1_long',
            'a2': 'a2_long',
            'a3': 'a3_long',
        }

    def tearDown(self):
        self.quantity_dict = None
        self.assumption_dict = None

    def test_quantity_string(self):
        string = quantity_string('T', self.quantity_dict)
        assert string == 'air temperature (K)'

    @raises(ValueError)
    def test_quantity_string_invalid_quantity(self):
        quantity_string('rhombus', self.quantity_dict)

    @raises(ValueError)
    def test_strings_to_list_string_empty(self):
        string = strings_to_list_string(())
        assert string == ''

    @raises(TypeError)
    def test_strings_to_list_string_input_string(self):
        strings_to_list_string('hello')

    def test_strings_to_list_string_single(self):
        string = strings_to_list_string(('string1',))
        assert string == 'string1'

    def test_strings_to_list_string_double(self):
        string = strings_to_list_string(('string1', 'string2'))
        assert string == 'string1 and string2'

    def test_strings_to_list_string_triple(self):
        string = strings_to_list_string(('string1', 'string2', 'string3'))
        assert string == 'string1, string2, and string3'

    @raises(ValueError)
    def test_assumption_list_string_empty(self):
        assumption_list_string((), self.assumption_dict)

    @raises(TypeError)
    def test_assumption_list_string_input_string(self):
        assumption_list_string('hello', self.assumption_dict)

    @raises(ValueError)
    def test_assumption_list_string_invalid(self):
        assumption_list_string(('ooglymoogly',), self.assumption_dict)

    def test_assumption_list_string_single(self):
        string = assumption_list_string(('a1',), self.assumption_dict)
        assert string == 'a1_long'

    def test_assumption_list_string_double(self):
        string = assumption_list_string(('a1', 'a2'), self.assumption_dict)
        assert string == 'a1_long and a2_long'

    def test_assumption_list_string_triple(self):
        string = assumption_list_string(('a1', 'a2', 'a3'),
                                        self.assumption_dict)
        assert string == 'a1_long, a2_long, and a3_long'

    @raises(ValueError)
    def test_quantity_spec_string_invalid(self):
        quantity_spec_string('rv', self.quantity_dict)

    def test_quantity_spec_string_valid(self):
        string = quantity_spec_string('T', self.quantity_dict)
        assert string == ('T : float or ndarray\n'
                          '    Data for air temperature (K).')

    def test_doc_paragraph_no_wrap_for_short_string(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over')
        assert '\n' not in string

    def test_doc_paragraph_wraps_on_word(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over.')
        if (string != 'The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\nover.'):
            raise AssertionError('incorrect string "{0}"'.format(string))

    def test_doc_paragraph_indent_wrap(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over',
                               indent=1)
        if (string != ' The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\n over'):
            raise AssertionError('incorrect string "{0}"'.format(string))

    def test_doc_paragraph_zero_indent(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over.',
                               indent=0)
        if (string != 'The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\nover.'):
            raise AssertionError('incorrect string "{0}"'.format(string))


class ParseDerivativeStringTests(unittest.TestCase):

    def setUp(self):
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }

    def tearDown(self):
        self.quantity_dict = None

    @raises(ValueError)
    def test_invalid_format(self):
        util.parse_derivative_string('ooglymoogly', self.quantity_dict)

    @raises(ValueError)
    def test_invalid_variable(self):
        util.parse_derivative_string('dpdz', self.quantity_dict)

    def test_dTdp(self):
        var1, var2 = util.parse_derivative_string('dTdp', self.quantity_dict)
        assert var1 == 'T'
        assert var2 == 'p'

    @raises(ValueError)
    def test_dpdT(self):
        util.parse_derivative_string('dpdT', self.quantity_dict)


class OverriddenByAssumptionsTests(unittest.TestCase):
    def test_overridden_by_assumptions_empty(self):
        @decorators.overridden_by_assumptions()
        def foo():
            return None
        assert foo.overridden_by_assumptions == ()

    def test_overridden_by_assumptions_single(self):
        @decorators.overridden_by_assumptions('test assumption')
        def foo():
            return None
        assert foo.overridden_by_assumptions == ('test assumption',)

    def test_overridden_by_assumptions_multiple(self):
        @decorators.overridden_by_assumptions('test assumption', 'a2')
        def foo():
            return None
        assert foo.overridden_by_assumptions == ('test assumption', 'a2')


class GetCalculatableMethodsDictTests(unittest.TestCase):

    def test_get_calculatable_methods_dict_empty(self):
        methods = {}
        out_methods = _get_calculatable_methods_dict((), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0

    def test_get_calculatable_methods_dict_returns_correct_type(self):
        methods = {'a': {('b',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b',), methods)
        assert isinstance(out_methods, dict)

    def test_get_calculatable_methods_dict_gets_single_method(self):
        methods = {'a': {('b',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b',), methods)
        assert 'a' in out_methods.keys()

    def test_get_calculatable_methods_dict_removes_correct_second_method(
            self):
        methods = {'a': {('b',): lambda x: x,
                         ('c', 'b'): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b', 'c'), methods)
        assert 'a' in out_methods.keys()
        assert ('b',) in out_methods['a'].keys()
        assert len(out_methods['a']) == 1

    def test_get_calculatable_methods_dict_removes_irrelevant_second_method(
            self):
        methods = {'a': {('b', 'd'): lambda x, y: x,
                         ('c',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b', 'd'), methods)
        assert 'a' in out_methods.keys()
        assert ('b', 'd') in out_methods['a'].keys()
        assert len(out_methods['a']) == 1

    def test_get_calculatable_methods_dict_gets_no_methods(self):
        methods = {'a': {('b',): lambda x: x},
                   'x': {('y', 'z'): lambda y, z: y*z}
                   }
        out_methods = _get_calculatable_methods_dict(('q',), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0

    def test_get_calculatable_methods_dict_doesnt_calculate_input(self):
        methods = {'a': {('b',): lambda x: x},
                   'x': {('y', 'z'): lambda y, z: y*z}
                   }
        out_methods = _get_calculatable_methods_dict(('b', 'a'), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0


class BaseSolverTests(unittest.TestCase):

    @raises(NotImplementedError)
    def test_cannot_instantiate(self):
        BaseSolver()


class FluidSolverTests(unittest.TestCase):

    def setUp(self):
        shape = (3, 4, 2, 2)
        self.vars1 = {'Tv': np.ones(shape),
                      'p': np.ones(shape)}
        self.vars2 = {'T': np.ones(shape),
                      'p': np.ones(shape)}

    def tearDown(self):
        self.vars1 = None
        self.vars2 = None

    def test_creation_no_arguments(self):
        FluidSolver()

    def test_is_instance_of_BaseSolver(self):
        deriv = FluidSolver()
        assert isinstance(deriv, BaseSolver)

    def test_creation_one_method(self):
        FluidSolver(assumptions=('hydrostatic',))

    def test_creation_compatible_methods(self):
        FluidSolver(assumptions=('hydrostatic', 'Tv equals T',))

    @raises(ValueError)
    def test_creation_incompatible_methods(self):
        FluidSolver(assumptions=('Goff-Gratch', 'Wexler',))

    @raises(ValueError)
    def test_creation_undefined_method(self):
        FluidSolver(assumptions=('moocow',))

    @raises(ValueError)
    def test_creation_undefined_method_with_defined_method(self):
        FluidSolver(assumptions=('hydrostatic', 'moocow',))

    def test_creation_with_vars(self):
        FluidSolver(**self.vars1)

    def test_creation_with_vars_and_method(self):
        FluidSolver(assumptions=('Tv equals T',), **self.vars1)

    def test_simple_calculation(self):
        deriver = FluidSolver(**self.vars1)
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)

    def test_depth_2_calculation(self):
        deriver = FluidSolver(assumptions=FluidSolver.default_assumptions +
                              ('Tv equals T',), **self.vars2)
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)

    def test_double_calculation(self):
        deriver = FluidSolver(add_assumptions=('Tv equals T',), **self.vars2)
        Tv = deriver.calculate('Tv')
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)
        assert isinstance(Tv, np.ndarray)

    def test_double_reverse_calculation(self):
        deriver = FluidSolver(add_assumptions=('Tv equals T',), **self.vars2)
        rho = deriver.calculate('rho')
        print('now Tv')
        Tv = deriver.calculate('Tv')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)
        assert isinstance(Tv, np.ndarray)


class calculateTests(unittest.TestCase):

    def setUp(self):
        self.shape = (3, 4, 2, 2)
        self.vars1 = {'Tv': np.ones(self.shape),
                      'p': np.ones(self.shape)}
        self.vars2 = {'T': np.ones(self.shape),
                      'p': np.ones(self.shape)}

    def tearDown(self):
        self.vars1 = None
        self.vars2 = None

    def test_simple_calculation(self):
        rho = calculate('rho', **self.vars1)
        assert (rho.shape == self.shape)
        assert (rho == 1/Rd).all()
        self.assertIsInstance(rho, np.ndarray)

    def test_returns_float(self):
        rho = calculate('rho', Tv=1., p=1.)
        self.assertIsInstance(rho, float)

    def test_depth_2_calculation(self):
        rho = calculate('rho', add_assumptions=('Tv equals T',), **self.vars2)
        assert rho.shape == self.shape
        assert (rho == 1/Rd).all()
        self.assertIsInstance(rho, np.ndarray)

    def test_double_calculation(self):
        Tv, rho = calculate('Tv', 'rho', add_assumptions=('Tv equals T',),
                            **self.vars2)
        assert Tv.shape == self.shape
        assert rho.shape == self.shape
        assert (rho == 1/Rd).all()
        self.assertIsInstance(rho, np.ndarray)
        self.assertIsInstance(Tv, np.ndarray)

    def test_double_reverse_calculation(self):
        rho, Tv = calculate('rho', 'Tv', add_assumptions=('Tv equals T',),
                            **self.vars2)
        assert (rho == 1/Rd).all()
        self.assertIsInstance(rho, np.ndarray)
        self.assertIsInstance(Tv, np.ndarray)

    def test_T_from_Tv(self):
        assert calculate('T', Tv=1., add_assumptions=('Tv equals T',)) == 1.
        assert calculate('T', Tv=5., add_assumptions=('Tv equals T',)) == 5.

    def test_rv_from_qv(self):
        self.assertAlmostEqual(calculate('rv', qv=0.005), 0.005025125628140704)

    def test_qv_from_rv(self):
        self.assertAlmostEqual(calculate('qv', rv=0.005), 0.004975124378109453)


class CalculateWithUnitsTests(unittest.TestCase):

    def setUp(self):
        self.units_dict = {
            'T': ('K', 'degC', 'degF'),
            'p': ('hPa', 'Pa'),
            'Tv': ('K', 'degC', 'degF'),
            'rv': ('g/kg', 'kg/kg'),<commit_msg>removed use of assertisinstance for 2.6 compatibility<commit_after># -*- coding: utf-8 -*-
"""
test.py: Testing suite for other modules.
"""
from __future__ import division, unicode_literals
import unittest
import nose
import numpy as np
from atmos import equations
from atmos import util
from atmos import decorators
from nose.tools import raises
from atmos.constants import Rd
from atmos.solve import BaseSolver, FluidSolver, calculate, \
    _get_module_methods, _get_calculatable_methods_dict,\
    _get_shortest_solution
from atmos.util import quantity_string, assumption_list_string, \
    quantity_spec_string, doc_paragraph, \
    strings_to_list_string
try:
    from inspect import getfullargspec
except ImportError:
    from inspect import getargspec as getfullargspec


def test_quantities_dict_complete():
    names = [item['output'] for item in _get_module_methods(equations)]
    for name in names:
        if name not in equations.quantities.keys():
            try:
                util.parse_derivative_string(name)
            except ValueError:
                raise AssertionError('{0} not in quantities dict'.format(name))


def test_get_module_methods_nonempty():
    result = _get_module_methods(equations)
    assert len(result) > 0


def test_default_assumptions_exist():
    for m in FluidSolver.default_assumptions:
        if m not in FluidSolver.all_assumptions:
            raise AssertionError('{0} not a valid assumption'.format(m))


class ddxTests(unittest.TestCase):

    def setUp(self):
        self.data = np.zeros((2, 3))
        self.data[:] = np.array([0., 5., 10.])[None, :]
        self.axis1 = np.array([5., 6., 7.])
        self.axis2 = np.array([[5., 6., 7.], [8., 10., 12.]])
        self.deriv1 = 5.*np.ones((2, 3))
        self.deriv2 = 5.*np.ones((2, 3))
        self.deriv2[1, :] *= 0.5

    @raises(ValueError)
    def test_invalid_data_axis(self):
        util.ddx(self.data, axis=2)

    @raises(ValueError)
    def test_invalid_axis_x(self):
        util.ddx(self.data, axis=2, x=self.axis1, axis_x=1)


class ClosestValTests(unittest.TestCase):

    def setUp(self):
        self.list = [1., 5., 10.]
        self.array = np.array([1., 5., 10.])

    def tearDown(self):
        self.list = None
        self.array = None

    @raises(ValueError)
    def testValueErrorOnEmptyList(self):
        util.closest_val(1., [])

    @raises(ValueError)
    def testValueErrorOnEmptyArray(self):
        util.closest_val(1., np.array([]))

    def testClosestValSingle(self):
        val = util.closest_val(1., np.array([50.]))
        assert val == 0

    def testClosestValInternal(self):
        val = util.closest_val(3.5, self.array)
        assert val == 1

    def testClosestValBelow(self):
        val = util.closest_val(-5., self.array)
        assert val == 0

    def testClosestValAbove(self):
        val = util.closest_val(20., self.array)
        assert val == 2

    def testClosestValInternalNegative(self):
        val = util.closest_val(-3.5, -1*self.array)
        assert val == 1

    def testClosestValInternalList(self):
        val = util.closest_val(3.5, self.list)
        assert val == 1

    def testClosestValBelowList(self):
        val = util.closest_val(-5., self.list)
        assert val == 0

    def testClosestValAboveList(self):
        val = util.closest_val(20., self.list)
        assert val == 2


class AreaPolySphereTests(unittest.TestCase):

    def setUp(self):
        self.lat1 = np.array([0.0, 90.0, 0.0])
        self.lon1 = np.array([0.0, 0.0, 90.0])
        self.area1 = 1.5708
        self.tol1 = 0.0001

    @raises(ValueError)
    def test_area_poly_sphere_insufficient_vertices(self):
        util.area_poly_sphere(self.lat1[:2], self.lon1[:2], 1.)

    def test_area_poly_sphere(self):
        area_calc = util.area_poly_sphere(self.lat1, self.lon1, 1.)
        assert abs(area_calc - self.area1) < self.tol1

    def test_area_poly_sphere_different_radius(self):
        area_calc = util.area_poly_sphere(self.lat1, self.lon1, 2.)
        assert abs(area_calc - 4*self.area1) < 4*self.tol1


class DecoratorTests(unittest.TestCase):

    def setUp(self):
        def dummyfunction(x):
            '''Dummy docstring'''
            return x
        self.func = dummyfunction
        self.func_name = dummyfunction.__name__
        self.func_argspec = getfullargspec(dummyfunction)
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }
        self.assumption_dict = {
            'a1': 'a1_long',
            'a2': 'a2_long',
            'a3': 'a3_long',
        }
        self.func_argspec = getfullargspec(self.func)

    def tearDown(self):
        self.func = None
        self.quantity_dict = None
        self.assumption_dict = None

    def test_assumes_empty(self, **kwargs):
        func = decorators.assumes()(self.func)
        assert func.assumptions == ()
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_assumes_single(self, **kwargs):
        func = decorators.assumes('a1')(self.func)
        assert func.assumptions == ('a1',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_assumes_triple(self, **kwargs):
        func = decorators.assumes('a1', 'a2', 'a3')(self.func)
        assert func.assumptions == ('a1', 'a2', 'a3',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_empty(self, **kwargs):
        func = decorators.overridden_by_assumptions()(self.func)
        assert func.overridden_by_assumptions == ()
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_single(self, **kwargs):
        func = decorators.overridden_by_assumptions('a1')(self.func)
        assert func.overridden_by_assumptions == ('a1',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    def test_overridden_by_assumptions_triple(self, **kwargs):
        func = decorators.overridden_by_assumptions(
            'a1', 'a2', 'a3')(self.func)
        assert func.overridden_by_assumptions == ('a1', 'a2', 'a3',)
        assert func.__name__ == self.func_name
        assert getfullargspec(func) == self.func_argspec

    @raises(ValueError)
    def test_autodoc_invalid_no_extra_args(self):
        def invalid_function(T, p):
            return T
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict)(invalid_function)

    @raises(ValueError)
    def test_autodoc_invalid_args_function(self):
        def T_from_x_y(x, y):
            return x
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict)(T_from_x_y)

    @raises(ValueError)
    def test_autodoc_invalid_extra_args(self):
        def invalid_function(T, p):
            return T
        decorators.equation_docstring(
            self.quantity_dict, self.assumption_dict, equation='x=y',
            references='reference here', notes='c sharp')(invalid_function)


class StringUtilityTests(unittest.TestCase):

    def setUp(self):
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }
        self.assumption_dict = {
            'a1': 'a1_long',
            'a2': 'a2_long',
            'a3': 'a3_long',
        }

    def tearDown(self):
        self.quantity_dict = None
        self.assumption_dict = None

    def test_quantity_string(self):
        string = quantity_string('T', self.quantity_dict)
        assert string == 'air temperature (K)'

    @raises(ValueError)
    def test_quantity_string_invalid_quantity(self):
        quantity_string('rhombus', self.quantity_dict)

    @raises(ValueError)
    def test_strings_to_list_string_empty(self):
        string = strings_to_list_string(())
        assert string == ''

    @raises(TypeError)
    def test_strings_to_list_string_input_string(self):
        strings_to_list_string('hello')

    def test_strings_to_list_string_single(self):
        string = strings_to_list_string(('string1',))
        assert string == 'string1'

    def test_strings_to_list_string_double(self):
        string = strings_to_list_string(('string1', 'string2'))
        assert string == 'string1 and string2'

    def test_strings_to_list_string_triple(self):
        string = strings_to_list_string(('string1', 'string2', 'string3'))
        assert string == 'string1, string2, and string3'

    @raises(ValueError)
    def test_assumption_list_string_empty(self):
        assumption_list_string((), self.assumption_dict)

    @raises(TypeError)
    def test_assumption_list_string_input_string(self):
        assumption_list_string('hello', self.assumption_dict)

    @raises(ValueError)
    def test_assumption_list_string_invalid(self):
        assumption_list_string(('ooglymoogly',), self.assumption_dict)

    def test_assumption_list_string_single(self):
        string = assumption_list_string(('a1',), self.assumption_dict)
        assert string == 'a1_long'

    def test_assumption_list_string_double(self):
        string = assumption_list_string(('a1', 'a2'), self.assumption_dict)
        assert string == 'a1_long and a2_long'

    def test_assumption_list_string_triple(self):
        string = assumption_list_string(('a1', 'a2', 'a3'),
                                        self.assumption_dict)
        assert string == 'a1_long, a2_long, and a3_long'

    @raises(ValueError)
    def test_quantity_spec_string_invalid(self):
        quantity_spec_string('rv', self.quantity_dict)

    def test_quantity_spec_string_valid(self):
        string = quantity_spec_string('T', self.quantity_dict)
        assert string == ('T : float or ndarray\n'
                          '    Data for air temperature (K).')

    def test_doc_paragraph_no_wrap_for_short_string(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over')
        assert '\n' not in string

    def test_doc_paragraph_wraps_on_word(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over.')
        if (string != 'The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\nover.'):
            raise AssertionError('incorrect string "{0}"'.format(string))

    def test_doc_paragraph_indent_wrap(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over',
                               indent=1)
        if (string != ' The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\n over'):
            raise AssertionError('incorrect string "{0}"'.format(string))

    def test_doc_paragraph_zero_indent(self):
        string = doc_paragraph('The quick brown fox jumped over the yellow '
                               'doge. The quick brown fox jumped over.',
                               indent=0)
        if (string != 'The quick brown fox jumped over the yellow doge. '
                'The quick brown fox jumped\nover.'):
            raise AssertionError('incorrect string "{0}"'.format(string))


class ParseDerivativeStringTests(unittest.TestCase):

    def setUp(self):
        self.quantity_dict = {
            'T': {'name': 'air temperature', 'units': 'K'},
            'qv': {'name': 'specific humidity', 'units': 'kg/kg'},
            'p': {'name': 'air pressure', 'units': 'Pa'},
        }

    def tearDown(self):
        self.quantity_dict = None

    @raises(ValueError)
    def test_invalid_format(self):
        util.parse_derivative_string('ooglymoogly', self.quantity_dict)

    @raises(ValueError)
    def test_invalid_variable(self):
        util.parse_derivative_string('dpdz', self.quantity_dict)

    def test_dTdp(self):
        var1, var2 = util.parse_derivative_string('dTdp', self.quantity_dict)
        assert var1 == 'T'
        assert var2 == 'p'

    @raises(ValueError)
    def test_dpdT(self):
        util.parse_derivative_string('dpdT', self.quantity_dict)


class OverriddenByAssumptionsTests(unittest.TestCase):
    def test_overridden_by_assumptions_empty(self):
        @decorators.overridden_by_assumptions()
        def foo():
            return None
        assert foo.overridden_by_assumptions == ()

    def test_overridden_by_assumptions_single(self):
        @decorators.overridden_by_assumptions('test assumption')
        def foo():
            return None
        assert foo.overridden_by_assumptions == ('test assumption',)

    def test_overridden_by_assumptions_multiple(self):
        @decorators.overridden_by_assumptions('test assumption', 'a2')
        def foo():
            return None
        assert foo.overridden_by_assumptions == ('test assumption', 'a2')


class GetCalculatableMethodsDictTests(unittest.TestCase):

    def test_get_calculatable_methods_dict_empty(self):
        methods = {}
        out_methods = _get_calculatable_methods_dict((), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0

    def test_get_calculatable_methods_dict_returns_correct_type(self):
        methods = {'a': {('b',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b',), methods)
        assert isinstance(out_methods, dict)

    def test_get_calculatable_methods_dict_gets_single_method(self):
        methods = {'a': {('b',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b',), methods)
        assert 'a' in out_methods.keys()

    def test_get_calculatable_methods_dict_removes_correct_second_method(
            self):
        methods = {'a': {('b',): lambda x: x,
                         ('c', 'b'): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b', 'c'), methods)
        assert 'a' in out_methods.keys()
        assert ('b',) in out_methods['a'].keys()
        assert len(out_methods['a']) == 1

    def test_get_calculatable_methods_dict_removes_irrelevant_second_method(
            self):
        methods = {'a': {('b', 'd'): lambda x, y: x,
                         ('c',): lambda x: x}}
        out_methods = _get_calculatable_methods_dict(('b', 'd'), methods)
        assert 'a' in out_methods.keys()
        assert ('b', 'd') in out_methods['a'].keys()
        assert len(out_methods['a']) == 1

    def test_get_calculatable_methods_dict_gets_no_methods(self):
        methods = {'a': {('b',): lambda x: x},
                   'x': {('y', 'z'): lambda y, z: y*z}
                   }
        out_methods = _get_calculatable_methods_dict(('q',), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0

    def test_get_calculatable_methods_dict_doesnt_calculate_input(self):
        methods = {'a': {('b',): lambda x: x},
                   'x': {('y', 'z'): lambda y, z: y*z}
                   }
        out_methods = _get_calculatable_methods_dict(('b', 'a'), methods)
        assert isinstance(out_methods, dict)
        assert len(out_methods) == 0


class BaseSolverTests(unittest.TestCase):

    @raises(NotImplementedError)
    def test_cannot_instantiate(self):
        BaseSolver()


class FluidSolverTests(unittest.TestCase):

    def setUp(self):
        shape = (3, 4, 2, 2)
        self.vars1 = {'Tv': np.ones(shape),
                      'p': np.ones(shape)}
        self.vars2 = {'T': np.ones(shape),
                      'p': np.ones(shape)}

    def tearDown(self):
        self.vars1 = None
        self.vars2 = None

    def test_creation_no_arguments(self):
        FluidSolver()

    def test_is_instance_of_BaseSolver(self):
        deriv = FluidSolver()
        assert isinstance(deriv, BaseSolver)

    def test_creation_one_method(self):
        FluidSolver(assumptions=('hydrostatic',))

    def test_creation_compatible_methods(self):
        FluidSolver(assumptions=('hydrostatic', 'Tv equals T',))

    @raises(ValueError)
    def test_creation_incompatible_methods(self):
        FluidSolver(assumptions=('Goff-Gratch', 'Wexler',))

    @raises(ValueError)
    def test_creation_undefined_method(self):
        FluidSolver(assumptions=('moocow',))

    @raises(ValueError)
    def test_creation_undefined_method_with_defined_method(self):
        FluidSolver(assumptions=('hydrostatic', 'moocow',))

    def test_creation_with_vars(self):
        FluidSolver(**self.vars1)

    def test_creation_with_vars_and_method(self):
        FluidSolver(assumptions=('Tv equals T',), **self.vars1)

    def test_simple_calculation(self):
        deriver = FluidSolver(**self.vars1)
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)

    def test_depth_2_calculation(self):
        deriver = FluidSolver(assumptions=FluidSolver.default_assumptions +
                              ('Tv equals T',), **self.vars2)
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)

    def test_double_calculation(self):
        deriver = FluidSolver(add_assumptions=('Tv equals T',), **self.vars2)
        Tv = deriver.calculate('Tv')
        rho = deriver.calculate('rho')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)
        assert isinstance(Tv, np.ndarray)

    def test_double_reverse_calculation(self):
        deriver = FluidSolver(add_assumptions=('Tv equals T',), **self.vars2)
        rho = deriver.calculate('rho')
        print('now Tv')
        Tv = deriver.calculate('Tv')
        assert (rho == 1/Rd).all()
        assert isinstance(rho, np.ndarray)
        assert isinstance(Tv, np.ndarray)


class calculateTests(unittest.TestCase):

    def setUp(self):
        self.shape = (3, 4, 2, 2)
        self.vars1 = {'Tv': np.ones(self.shape),
                      'p': np.ones(self.shape)}
        self.vars2 = {'T': np.ones(self.shape),
                      'p': np.ones(self.shape)}

    def tearDown(self):
        self.vars1 = None
        self.vars2 = None

    def test_simple_calculation(self):
        rho = calculate('rho', **self.vars1)
        assert (rho.shape == self.shape)
        assert (rho == 1/Rd).all()
        self.assertTrue(isinstance(rho, np.ndarray),
                        'returned rho should be ndarray')

    def test_returns_float(self):
        rho = calculate('rho', Tv=1., p=1.)
        self.assertTrue(isinstance(rho, float),
                        'returned rho should be float')

    def test_depth_2_calculation(self):
        rho = calculate('rho', add_assumptions=('Tv equals T',), **self.vars2)
        assert rho.shape == self.shape
        assert (rho == 1/Rd).all()
        self.assertTrue(isinstance(rho, np.ndarray),
                        'returned rho should be ndarray')

    def test_double_calculation(self):
        Tv, rho = calculate('Tv', 'rho', add_assumptions=('Tv equals T',),
                            **self.vars2)
        assert Tv.shape == self.shape
        assert rho.shape == self.shape
        assert (rho == 1/Rd).all()
        self.assertTrue(isinstance(rho, np.ndarray),
                        'returned rho should be ndarray')
        self.assertTrue(isinstance(Tv, np.ndarray),
                        'returned Tv should be ndarray')

    def test_double_reverse_calculation(self):
        rho, Tv = calculate('rho', 'Tv', add_assumptions=('Tv equals T',),
                            **self.vars2)
        assert (rho == 1/Rd).all()
        self.assertTrue(isinstance(rho, np.ndarray),
                        'returned rho should be ndarray')
        self.assertTrue(isinstance(Tv, np.ndarray),
                        'returned Tv should be ndarray')

    def test_T_from_Tv(self):
        assert calculate('T', Tv=1., add_assumptions=('Tv equals T',)) == 1.
        assert calculate('T', Tv=5., add_assumptions=('Tv equals T',)) == 5.

    def test_rv_from_qv(self):
        self.assertAlmostEqual(calculate('rv', qv=0.005), 0.005025125628140704)

    def test_qv_from_rv(self):
        self.assertAlmostEqual(calculate('qv', rv=0.005), 0.004975124378109453)


class CalculateWithUnitsTests(unittest.TestCase):

    def setUp(self):
        self.units_dict = {
            'T': ('K', 'degC', 'degF'),
            'p': ('hPa', 'Pa'),
            'Tv': ('K', 'degC', 'degF'),
            'rv': ('g/kg', 'kg/kg'),
{'fraction_non_alphanumeric': 0.1234126792525711, 'fraction_numerical': 0.013277968229443292, 'mean_word_length': 2.694853269110695, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6365998', 'n_tokens_mistral': 13753, 'n_tokens_neox': 13065, 'n_words': 2662}