<commit_before># Author: Enric Tejedor, Danilo Piparo CERN  06/2018

################################################################################
# Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.                      #
# All rights reserved.                                                         #
#                                                                              #
# For the licensing terms see $ROOTSYS/LICENSE.                                #
# For the list of contributors see $ROOTSYS/README/CREDITS.                    #
################################################################################

import importlib
import inspect
import pkgutil
import re
import sys

import cppyy
gbl_namespace = cppyy.gbl

from ._generic import pythonize_generic

def pythonization(class_name, ns='::', is_prefix=False):
    '''
    Decorator that allows to pythonize C++ classes. To pythonize means to add
    some extra behaviour to a C++ class that is used from Python via PyROOT,
    so that such a class can be used in an easier / more "pythonic" way.
    The injection of the pythonization behaviour in the C++ class is done
    lazily, i.e. only when the class is first accessed from the application.

    Args:
        target (string/iterable[string]): specifies either a single string or
            multiple strings, where each string can be either (i) the
            fully-qualified name of a C++ class to be pythonized, or (ii) a
            prefix to match all classes whose fully-qualified name starts with
            that prefix.

            These are examples of prefixes and what they match:
            - "" : all classes in the global namespace
            - "C" : all classes in the global namespace whose name starts with
              "C"
            - "NS1::NS2::" : all classes in namespace "NS1::NS2"
            - "NS1::NS2::C" : all classes in namespace "NS1::NS2" whose name
              starts with "C"

        is_prefix (boolean): if True, `target` contains one or multiple
            prefixes, each prefix potentially matching multiple classes.
            Default is False.

    Returns:
        function: function that receives the user-defined function and
            decorates it.
    '''

    # Type check and parsing of target argument.
    # Retrieve the scope(s) of the class(es)/prefix(es) to register the
    # pythonizor in the right scope(s)
    target = _check_target(class_name)

    # Remove trailing '::' from namespace
    if ns.endswith('::'):
        ns = ns[:-2]

    # Create a filter lambda for the target class(es)/prefix(es)
    if is_prefix:
        def passes_filter(class_name):
            return any(class_name.startswith(prefix)
                       for prefix in target)
    else:
        def passes_filter(class_name):
            return class_name in target

    def pythonization_impl(user_pythonizor):
        '''
        The real decorator. Accepts a user-provided function and decorates it.
        An inner function - a wrapper of the user function - is registered in
        cppyy as a pythonizor.

        Args:
            user_pythonizor (function): user-provided function to be decorated.
                It implements some pythonization. It must accept two
                parameters: the class to be pythonized, i.e. the Python proxy
                of the class in which new behaviour can be injected, and the
                name of that class (can be used e.g. to do some more complex
                filtering).
        '''

        npars = _check_num_pars(user_pythonizor)

        # Check whether any of the target classes has already been used.
        # If so, the class proxy has to be immediately pythonized - even if we
        # registered a pythonizor for it, the pythonizor would never be executed
        _find_used_classes(ns, passes_filter, user_pythonizor, npars)

        def cppyy_pythonizor(klass, name):
            '''
            Wrapper function with the parameters that cppyy requires for a
            pythonizor function (class proxy and class name). It invokes the
            user function only if the current class - a candidate for being
            pythonized - matches the `target` argument of the decorator.

            Args:
                klass (class type): cppyy proxy of the class that is the
                    current candidate to be pythonized.
                name (string): name of the class that is the current candidate
                    to be pythonized.
            '''

            fqn = klass.__cpp_name__

            # Add pretty printing (done on all classes)
            pythonize_generic(klass, fqn)

            if passes_filter(name):
                _invoke(user_pythonizor, npars, klass, fqn)

        # Register pythonizor in the corresponding namespace
        cppyy.py.add_pythonization(cppyy_pythonizor, ns)

        # Return the original user function.
        # We don't want to modify the user function, we just use the decorator
        # to register the function as a pythonizor.
        # This allows for correct chaining of multiple @pythonization decorators
        # for a single function
        return user_pythonizor

    return pythonization_impl

def _check_target(target):
    '''
    Helper function to check the type of the `class name` argument specified by
    the user in a @pythonization decorator.

    Args:
        target (string/iterable[string]): class name(s)/prefix(es).

    Returns:
        list[string]: class name(s)/prefix(es) in `target`, with no repetitions.
    '''

    if _is_string(target):
        _check_no_namespace(target)
        target = [ target ]
    else:
        for name in target:
            if _is_string(name):
                _check_no_namespace(name)
            else:
                raise TypeError('Invalid type of "target" argument in '
                                '@pythonization: must be string or iterable of '
                                'strings')
        # Remove possible duplicates
        target = list(set(target))

    return target

def _is_string(o):
    '''
    Checks if object is a string.

    Args:
        o (any type): object to be checked.

    Returns:
        True if object is a string, False otherwise.
    '''

    if sys.version_info >= (3, 0):
        return isinstance(o, str)
    else:
        return isinstance(o, basestring)

def _check_no_namespace(target):
    '''
    Checks that a given target of a pythonizor does not specify a namespace
    (only the class name / prefix of a class name should be present).

    Args:
        target (string): class name/prefix.
    '''

    if target.find('::') >= 0:
        raise ValueError('Invalid value of "class_name" argument in '
                         '@pythonization: namespace definition found ("{}"). '
                         'Please use the "ns" parameter to specify the '
                         'namespace'.format(target))

def _check_num_pars(f):
    '''
    Checks the number of parameters of the `f` function.

    Args:
        f (function): user pythonizor function.

    Returns:
        int: number of positional parameters of `f`.
    '''

    if sys.version_info >= (3, 0):
        npars = len(inspect.getfullargspec(f).args)
    else:
        npars = len(inspect.getargspec(f).args)

    if npars == 0 or npars > 2:
        raise TypeError('Pythonizor function {} has a wrong number of '
                        'parameters ({}). Allowed parameters are the class to '
                        'be pythonized and (optionally) its name.'
                        .format(f.__name__, npars))

    return npars

def _invoke(user_pythonizor, npars, klass, fqn):
    '''
    Invokes the given user pythonizor function with the right arguments.

    Args:
        user_pythonizor (function): user pythonizor function.
        npars (int): number of parameters of the user pythonizor function.
        klass (class type): cppyy proxy of the class to be pythonized.
        fqn (string): fully-qualified name of the class to be pythonized.
    '''

    if npars == 1:
        user_pythonizor(klass)
    else:
        user_pythonizor(klass, fqn)

def _find_used_classes(ns, passes_filter, user_pythonizor, npars):
    '''
    Finds already instantiated classes in namespace `ns` that pass the filter
    of `passes_filter`. Every matching class is pythonized with the
    `user_pythonizor` function.
    This makes sure a pythonizor is also applied to classes that have already
    been used at the time the pythonizor is registered.

    Args:
        ns (string): namespace of the class names of prefixes in `targets`.
        passes_filter (function): function that determines if a given class
            is the target of `user_pythonizor`.
        user_pythonizor (function): user pythonizor function.
        npars (int): number of parameters of the user pythonizor function.
    '''

    ns_obj = _find_namespace(ns)
    if ns_obj is None:
        # Namespace has not been used yet, no need to inspect more
        return

    ns_vars = vars(ns_obj)
    for var_name, var_value in ns_vars.items():
        if str(var_value).startswith('<class cppyy.gbl.'):
            # It's a class proxy, check if name matches
            parsed_var_name = _get_class_name(var_name)
            if passes_filter(parsed_var_name):
                # Pythonize right away!
                _invoke(user_pythonizor, npars, var_value, var_value.__cpp_name__)

def _find_namespace(ns):
    '''
    Finds and returns the proxy object of the `ns` namespace, if it has already
    been accessed.

    Args:
        ns (string): a namespace.

    Returns:
        namespace proxy object, if the namespace has already been accessed,
            otherwise None.
    '''

    if ns == '':
        return gbl_namespace

    ns_obj = gbl_namespace
    # Get all namespaces in a list
    every_ns = ns.split('::')
    for ns in every_ns:
        ns_vars = vars(ns_obj)
        if not ns in ns_vars:
            return None
        ns_obj = getattr(ns_obj, ns)

    return ns_obj

def _get_class_name(fqn):
    '''
    Parses and returns the class name in `fqn`.
    Example: if `fqn` is "NS1::NS2::C", "C" is returned.

    Args:
        fqn (string): fully-qualified class name.

    Returns:
        string: class name in `fqn`.
    '''

    pos = _find_namespace_end(fqn)
    if pos < 0: # no namespace found
        return fqn
    else:
        return fqn[pos+2:]

def _find_namespace_end(fqn):
    '''
    Find the position where the namespace in `fqn` ends.

    Args:
        fqn (string): fully-qualified class name.

    Returns:
        integer: position where the namespace in `fqn` ends, i.e. the position
            of the last '::', or -1 if there is no '::' in `fqn`.
    '''

    last_found = -1
    prev_c = ''
    pos = 0
    for c in fqn:
        if c == ':' and prev_c == ':':
            last_found = pos - 1
        elif c == '<':
            # If we found a template, this is already the class name,
            # so we're done!
            break
        prev_c = c
        pos += 1

    return last_found

def _register_pythonizations():
    '''
    Registers the ROOT pythonizations with cppyy for lazy injection.
    '''

    exclude = [ '_rdf_utils' ]
    for _, module_name, _ in  pkgutil.walk_packages(__path__):
        if module_name not in exclude:
            importlib.import_module(__name__ + '.' + module_name)
<commit_msg>[PyROOT] Update docs of pythonization decorator<commit_after># Author: Enric Tejedor, Danilo Piparo CERN  06/2018

################################################################################
# Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.                      #
# All rights reserved.                                                         #
#                                                                              #
# For the licensing terms see $ROOTSYS/LICENSE.                                #
# For the list of contributors see $ROOTSYS/README/CREDITS.                    #
################################################################################

import importlib
import inspect
import pkgutil
import re
import sys

import cppyy
gbl_namespace = cppyy.gbl

from ._generic import pythonize_generic

def pythonization(class_name, ns='::', is_prefix=False):
    '''
    Decorator that allows to pythonize C++ classes. To pythonize means to add
    some extra behaviour to a C++ class that is used from Python via PyROOT,
    so that such a class can be used in an easier / more "pythonic" way.
    When a pythonization is registered with this decorator, the injection of
    the new behaviour in the C++ class is done immediately, if the class has
    already been used from the application, or lazily, i.e. only when the class
    is first accessed from the application.

    Args:
        class_name (string/iterable[string]): specifies either a single string or
            multiple strings, where each string can be either (i) the name of a
            C++ class to be pythonized, or (ii) a prefix to match all classes
            whose name starts with that prefix.
        ns (string): namespace of the classes to be pythonized. Default is the
            global namespace (`::`).
        is_prefix (boolean): if True, `class_name` contains one or multiple
            prefixes, each prefix potentially matching multiple classes.
            Default is False.
            These are examples of prefixes and namespace and what they match:
            - class_name="", ns="::" : all classes in the global namespace.
            - class_name="C", ns="::" : all classes in the global namespace
              whose name starts with "C"
            - class_name="", ns="NS1::NS2" : all classes in namespace "NS1::NS2"
            - class_name="C", ns="NS1::NS2" : all classes in namespace
              "NS1::NS2" whose name starts with "C"

    Returns:
        function: function that receives the user-defined function and
            decorates it.
    '''

    # Type check and parsing of target argument.
    # Retrieve the scope(s) of the class(es)/prefix(es) to register the
    # pythonizor in the right scope(s)
    target = _check_target(class_name)

    # Remove trailing '::' from namespace
    if ns.endswith('::'):
        ns = ns[:-2]

    # Create a filter lambda for the target class(es)/prefix(es)
    if is_prefix:
        def passes_filter(class_name):
            return any(class_name.startswith(prefix)
                       for prefix in target)
    else:
        def passes_filter(class_name):
            return class_name in target

    def pythonization_impl(user_pythonizor):
        '''
        The real decorator. Accepts a user-provided function and decorates it.
        An inner function - a wrapper of the user function - is registered in
        cppyy as a pythonizor.

        Args:
            user_pythonizor (function): user-provided function to be decorated.
                It implements some pythonization. It can accept two parameters:
                the class to be pythonized, i.e. the Python proxy of the class
                in which new behaviour can be injected, and optionally the name
                of that class (can be used e.g. to do some more complex
                filtering).

        Returns:
            function: the user function, after being registered as a
                pythonizor.
        '''

        npars = _check_num_pars(user_pythonizor)

        # Check whether any of the target classes has already been used.
        # If so, the class proxy has to be immediately pythonized - even if we
        # registered a pythonizor for it, the pythonizor would never be executed
        _find_used_classes(ns, passes_filter, user_pythonizor, npars)

        def cppyy_pythonizor(klass, name):
            '''
            Wrapper function with the parameters that cppyy requires for a
            pythonizor function (class proxy and class name). It invokes the
            user function only if the current class - a candidate for being
            pythonized - matches the `target` argument of the decorator.

            Args:
                klass (class type): cppyy proxy of the class that is the
                    current candidate to be pythonized.
                name (string): name of the class that is the current candidate
                    to be pythonized.
            '''

            fqn = klass.__cpp_name__

            # Add pretty printing (done on all classes)
            pythonize_generic(klass, fqn)

            if passes_filter(name):
                _invoke(user_pythonizor, npars, klass, fqn)

        # Register pythonizor in its namespace
        cppyy.py.add_pythonization(cppyy_pythonizor, ns)

        # Return the original user function.
        # We don't want to modify the user function, we just use the decorator
        # to register the function as a pythonizor.
        # This allows for correct chaining of multiple @pythonization decorators
        # for a single function
        return user_pythonizor

    return pythonization_impl

def _check_target(target):
    '''
    Helper function to check the type of the `class name` argument specified by
    the user in a @pythonization decorator.

    Args:
        target (string/iterable[string]): class name(s)/prefix(es).

    Returns:
        list[string]: class name(s)/prefix(es) in `target`, with no repetitions.
    '''

    if _is_string(target):
        _check_no_namespace(target)
        target = [ target ]
    else:
        for name in target:
            if _is_string(name):
                _check_no_namespace(name)
            else:
                raise TypeError('Invalid type of "target" argument in '
                                '@pythonization: must be string or iterable of '
                                'strings')
        # Remove possible duplicates
        target = list(set(target))

    return target

def _is_string(o):
    '''
    Checks if object is a string.

    Args:
        o (any type): object to be checked.

    Returns:
        True if object is a string, False otherwise.
    '''

    if sys.version_info >= (3, 0):
        return isinstance(o, str)
    else:
        return isinstance(o, basestring)

def _check_no_namespace(target):
    '''
    Checks that a given target of a pythonizor does not specify a namespace
    (only the class name / prefix of a class name should be present).

    Args:
        target (string): class name/prefix.
    '''

    if target.find('::') >= 0:
        raise ValueError('Invalid value of "class_name" argument in '
                         '@pythonization: namespace definition found ("{}"). '
                         'Please use the "ns" parameter to specify the '
                         'namespace'.format(target))

def _check_num_pars(f):
    '''
    Checks the number of parameters of the `f` function.

    Args:
        f (function): user pythonizor function.

    Returns:
        int: number of positional parameters of `f`.
    '''

    if sys.version_info >= (3, 0):
        npars = len(inspect.getfullargspec(f).args)
    else:
        npars = len(inspect.getargspec(f).args)

    if npars == 0 or npars > 2:
        raise TypeError('Pythonizor function {} has a wrong number of '
                        'parameters ({}). Allowed parameters are the class to '
                        'be pythonized and (optionally) its name.'
                        .format(f.__name__, npars))

    return npars

def _invoke(user_pythonizor, npars, klass, fqn):
    '''
    Invokes the given user pythonizor function with the right arguments.

    Args:
        user_pythonizor (function): user pythonizor function.
        npars (int): number of parameters of the user pythonizor function.
        klass (class type): cppyy proxy of the class to be pythonized.
        fqn (string): fully-qualified name of the class to be pythonized.
    '''

    if npars == 1:
        user_pythonizor(klass)
    else:
        user_pythonizor(klass, fqn)

def _find_used_classes(ns, passes_filter, user_pythonizor, npars):
    '''
    Finds already instantiated classes in namespace `ns` that pass the filter
    of `passes_filter`. Every matching class is pythonized with the
    `user_pythonizor` function.
    This makes sure a pythonizor is also applied to classes that have already
    been used at the time the pythonizor is registered.

    Args:
        ns (string): namespace of the class names of prefixes in `targets`.
        passes_filter (function): function that determines if a given class
            is the target of `user_pythonizor`.
        user_pythonizor (function): user pythonizor function.
        npars (int): number of parameters of the user pythonizor function.
    '''

    ns_obj = _find_namespace(ns)
    if ns_obj is None:
        # Namespace has not been used yet, no need to inspect more
        return

    ns_vars = vars(ns_obj)
    for var_name, var_value in ns_vars.items():
        if str(var_value).startswith('<class cppyy.gbl.'):
            # It's a class proxy, check if name matches
            parsed_var_name = _get_class_name(var_name)
            if passes_filter(parsed_var_name):
                # Pythonize right away!
                _invoke(user_pythonizor, npars, var_value, var_value.__cpp_name__)

def _find_namespace(ns):
    '''
    Finds and returns the proxy object of the `ns` namespace, if it has already
    been accessed.

    Args:
        ns (string): a namespace.

    Returns:
        namespace proxy object, if the namespace has already been accessed,
            otherwise None.
    '''

    if ns == '':
        return gbl_namespace

    ns_obj = gbl_namespace
    # Get all namespaces in a list
    every_ns = ns.split('::')
    for ns in every_ns:
        ns_vars = vars(ns_obj)
        if not ns in ns_vars:
            return None
        ns_obj = getattr(ns_obj, ns)

    return ns_obj

def _get_class_name(fqn):
    '''
    Parses and returns the class name in `fqn`.
    Example: if `fqn` is "NS1::NS2::C", "C" is returned.

    Args:
        fqn (string): fully-qualified class name.

    Returns:
        string: class name in `fqn`.
    '''

    pos = _find_namespace_end(fqn)
    if pos < 0: # no namespace found
        return fqn
    else:
        return fqn[pos+2:]

def _find_namespace_end(fqn):
    '''
    Find the position where the namespace in `fqn` ends.

    Args:
        fqn (string): fully-qualified class name.

    Returns:
        integer: position where the namespace in `fqn` ends, i.e. the position
            of the last '::', or -1 if there is no '::' in `fqn`.
    '''

    last_found = -1
    prev_c = ''
    pos = 0
    for c in fqn:
        if c == ':' and prev_c == ':':
            last_found = pos - 1
        elif c == '<':
            # If we found a template, this is already the class name,
            # so we're done!
            break
        prev_c = c
        pos += 1

    return last_found

def _register_pythonizations():
    '''
    Registers the ROOT pythonizations with cppyy for lazy injection.
    '''

    exclude = [ '_rdf_utils' ]
    for _, module_name, _ in  pkgutil.walk_packages(__path__):
        if module_name not in exclude:
            importlib.import_module(__name__ + '.' + module_name)
<|endoftext|>


<commit_before>import os
import time

from flask import request
from mock import patch
from sqlalchemy.orm.exc import NoResultFound
from tests import BaseTestCase

from redash import models, settings
from redash.authentication import (api_key_load_user_from_request,
                                   get_login_url, hmac_load_user_from_request,
                                   sign)
from redash.authentication.google_oauth import (create_and_login_user,
                                                verify_profile)

class TestApiKeyAuthentication(BaseTestCase):
    #
    # This is a bad way to write these tests, but the way Flask works doesn't make it easy to write them properly...
    #
    def setUp(self):
        super(TestApiKeyAuthentication, self).setUp()
        self.api_key = '10'
        self.query = self.factory.create_query(api_key=self.api_key)
        models.db.session.flush()
        self.query_url = '/{}/api/queries/{}'.format(self.factory.org.slug, self.query.id)
        self.queries_url = '/{}/api/queries'.format(self.factory.org.slug)

    def test_no_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url)
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_wrong_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, query_string={'api_key': 'whatever'})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_correct_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, query_string={'api_key': self.api_key})
            self.assertIsNotNone(api_key_load_user_from_request(request))

    def test_no_query_id(self):
        with self.app.test_client() as c:
            rv = c.get(self.queries_url, query_string={'api_key': self.api_key})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_user_api_key(self):
        user = self.factory.create_user(api_key="user_key")
        models.db.session.flush()
        with self.app.test_client() as c:
            rv = c.get(self.queries_url, query_string={'api_key': user.api_key})
            self.assertEqual(user.id, api_key_load_user_from_request(request).id)

    def test_api_key_header(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key {}".format(self.api_key)})
            self.assertIsNotNone(api_key_load_user_from_request(request))

    def test_api_key_header_with_wrong_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key oops"})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_api_key_for_wrong_org(self):
        other_user = self.factory.create_admin(org=self.factory.create_org())

        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key {}".format(other_user.api_key)})
            self.assertEqual(404, rv.status_code)

class TestHMACAuthentication(BaseTestCase):
    #
    # This is a bad way to write these tests, but the way Flask works doesn't make it easy to write them properly...
    #
    def setUp(self):
        super(TestHMACAuthentication, self).setUp()
        self.api_key = '10'
        self.query = self.factory.create_query(api_key=self.api_key)
        models.db.session.flush()
        self.path = '/{}/api/queries/{}'.format(self.query.org.slug, self.query.id)
        self.expires = time.time() + 1800

    def signature(self, expires):
        return sign(self.query.api_key, self.path, expires)

    def test_no_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path)
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_wrong_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path, query_string={'signature': 'whatever', 'expires': self.expires})
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_correct_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path, query_string={'signature': self.signature(self.expires), 'expires': self.expires})
            self.assertIsNotNone(hmac_load_user_from_request(request))

    def test_no_query_id(self):
        with self.app.test_client() as c:
            rv = c.get('/{}/api/queries'.format(self.query.org.slug), query_string={'api_key': self.api_key})
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_user_api_key(self):
        user = self.factory.create_user(api_key="user_key")
        path = '/api/queries/'
        models.db.session.flush()

        signature = sign(user.api_key, path, self.expires)
        with self.app.test_client() as c:
            rv = c.get(path, query_string={'signature': signature, 'expires': self.expires, 'user_id': user.id})
            self.assertEqual(user.id, hmac_load_user_from_request(request).id)

class TestCreateAndLoginUser(BaseTestCase):
    def test_logins_valid_user(self):
        user = self.factory.create_user(email='test@example.com')

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, user.name, user.email)
            login_user_mock.assert_called_once_with(user, remember=True)

    def test_creates_vaild_new_user(self):
        email = 'test@example.com'
        name = 'Test User'

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, name, email)

            self.assertTrue(login_user_mock.called)
            user = models.User.query.filter(models.User.email == email).one()
            self.assertEqual(user.email, email)

    def test_updates_user_name(self):
        user = self.factory.create_user(email='test@example.com')

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, "New Name", user.email)
            login_user_mock.assert_called_once_with(user, remember=True)

class TestVerifyProfile(BaseTestCase):
    def test_no_domain_allowed_for_org(self):
        profile = dict(email='arik@example.com')
        self.assertFalse(verify_profile(self.factory.org, profile))

    def test_domain_not_in_org_domains_list(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org']
        self.assertFalse(verify_profile(self.factory.org, profile))

    def test_domain_in_org_domains_list(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.com']
        self.assertTrue(verify_profile(self.factory.org, profile))

        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org', 'example.com']
        self.assertTrue(verify_profile(self.factory.org, profile))

    def test_org_in_public_mode_accepts_any_domain(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_IS_PUBLIC] = True
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = []
        self.assertTrue(verify_profile(self.factory.org, profile))

    def test_user_not_in_domain_but_account_exists(self):
        profile = dict(email='arik@example.com')
        self.factory.create_user(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org']
        self.assertTrue(verify_profile(self.factory.org, profile))

class TestGetLoginUrl(BaseTestCase):
    def test_when_multi_org_enabled_and_org_exists(self):
        with self.app.test_request_context('/{}/'.format(self.factory.org.slug)):
            self.assertEqual(get_login_url(next=None), '/{}/login'.format(self.factory.org.slug))

    def test_when_multi_org_enabled_and_org_doesnt_exist(self):
        with self.app.test_request_context('/{}_notexists/'.format(self.factory.org.slug)):
            self.assertEqual(get_login_url(next=None), '/')

class TestRemoteUserAuth(BaseTestCase):
    DEFAULT_SETTING_OVERRIDES = {
        'REDASH_REMOTE_USER_LOGIN_ENABLED': 'true'
    }

    def setUp(self):
        # Apply default setting overrides to every test
        self.overrideSettings(None)

        super(TestRemoteUserAuth, self).setUp()

    def overrideSettings(self, overrides):
        """Override settings for testing purposes.

        This helper method can be used to override specific environmental
        variables to enable / disable Re:Dash features for the duration
        of the test.

        Note that these overrides only affect code that checks the value of
        the setting at runtime. It doesn't affect code that only checks the
        value during program initialization.

        :param dict overrides: a dict of environmental variables to override
            when the settings are reloaded
        """
        variables = self.DEFAULT_SETTING_OVERRIDES.copy()
        variables.update(overrides or {})
        with patch.dict(os.environ, variables):
            reload(settings)

        # Queue a cleanup routine that reloads the settings without overrides
        # once the test ends
        self.addCleanup(lambda: reload(settings))

    def assertCorrectUserAttributes(self, user, email='test@example.com', name='test@example.com', groups=None, org=None):
        """Helper to assert that the user attributes are correct."""
        groups = groups or []
        if self.factory.org.default_group.id not in groups:
            groups.append(self.factory.org.default_group.id)

        self.assertIsNotNone(user)
        self.assertEqual(user.email, email)
        self.assertEqual(user.name, name)
        self.assertEqual(user.org, org or self.factory.org)
        self.assertItemsEqual(user.group_ids, groups)

    def getTestUser(self, email='test@example.com', org=None):
        """Helper to fetch an user from the database."""

        # Expire all cached objects to ensure these values are read directly
        # from the database.
        models.db.session.expire_all()

        return models.User.get_by_email_and_org(email, org or self.factory.org)

    def test_remote_login_disabled(self):
        self.overrideSettings({
            'REDASH_REMOTE_USER_LOGIN_ENABLED': 'false'
        })

        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Forwarded-Remote-User': 'test@example.com'
        })

        with self.assertRaises(NoResultFound):
            self.getTestUser()

    def test_remote_login_default_header(self):
        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Forwarded-Remote-User': 'test@example.com'
        })

        self.assertCorrectUserAttributes(self.getTestUser())

    def test_remote_login_custom_header(self):
        self.overrideSettings({
            'REDASH_REMOTE_USER_HEADER': 'X-Custom-User'
        })

        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Custom-User': 'test@example.com'
        })

        self.assertCorrectUserAttributes(self.getTestUser())
<commit_msg>followup: coding style fixes to tests<commit_after>import os
import time

from flask import request
from mock import patch
from sqlalchemy.orm.exc import NoResultFound
from tests import BaseTestCase

from redash import models, settings
from redash.authentication import (api_key_load_user_from_request,
                                   get_login_url, hmac_load_user_from_request,
                                   sign)
from redash.authentication.google_oauth import (create_and_login_user,
                                                verify_profile)

class TestApiKeyAuthentication(BaseTestCase):
    #
    # This is a bad way to write these tests, but the way Flask works doesn't make it easy to write them properly...
    #
    def setUp(self):
        super(TestApiKeyAuthentication, self).setUp()
        self.api_key = '10'
        self.query = self.factory.create_query(api_key=self.api_key)
        models.db.session.flush()
        self.query_url = '/{}/api/queries/{}'.format(self.factory.org.slug, self.query.id)
        self.queries_url = '/{}/api/queries'.format(self.factory.org.slug)

    def test_no_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url)
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_wrong_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, query_string={'api_key': 'whatever'})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_correct_api_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, query_string={'api_key': self.api_key})
            self.assertIsNotNone(api_key_load_user_from_request(request))

    def test_no_query_id(self):
        with self.app.test_client() as c:
            rv = c.get(self.queries_url, query_string={'api_key': self.api_key})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_user_api_key(self):
        user = self.factory.create_user(api_key="user_key")
        models.db.session.flush()
        with self.app.test_client() as c:
            rv = c.get(self.queries_url, query_string={'api_key': user.api_key})
            self.assertEqual(user.id, api_key_load_user_from_request(request).id)

    def test_api_key_header(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key {}".format(self.api_key)})
            self.assertIsNotNone(api_key_load_user_from_request(request))

    def test_api_key_header_with_wrong_key(self):
        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key oops"})
            self.assertIsNone(api_key_load_user_from_request(request))

    def test_api_key_for_wrong_org(self):
        other_user = self.factory.create_admin(org=self.factory.create_org())

        with self.app.test_client() as c:
            rv = c.get(self.query_url, headers={'Authorization': "Key {}".format(other_user.api_key)})
            self.assertEqual(404, rv.status_code)

class TestHMACAuthentication(BaseTestCase):
    #
    # This is a bad way to write these tests, but the way Flask works doesn't make it easy to write them properly...
    #
    def setUp(self):
        super(TestHMACAuthentication, self).setUp()
        self.api_key = '10'
        self.query = self.factory.create_query(api_key=self.api_key)
        models.db.session.flush()
        self.path = '/{}/api/queries/{}'.format(self.query.org.slug, self.query.id)
        self.expires = time.time() + 1800

    def signature(self, expires):
        return sign(self.query.api_key, self.path, expires)

    def test_no_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path)
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_wrong_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path, query_string={'signature': 'whatever', 'expires': self.expires})
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_correct_signature(self):
        with self.app.test_client() as c:
            rv = c.get(self.path, query_string={'signature': self.signature(self.expires), 'expires': self.expires})
            self.assertIsNotNone(hmac_load_user_from_request(request))

    def test_no_query_id(self):
        with self.app.test_client() as c:
            rv = c.get('/{}/api/queries'.format(self.query.org.slug), query_string={'api_key': self.api_key})
            self.assertIsNone(hmac_load_user_from_request(request))

    def test_user_api_key(self):
        user = self.factory.create_user(api_key="user_key")
        path = '/api/queries/'
        models.db.session.flush()

        signature = sign(user.api_key, path, self.expires)
        with self.app.test_client() as c:
            rv = c.get(path, query_string={'signature': signature, 'expires': self.expires, 'user_id': user.id})
            self.assertEqual(user.id, hmac_load_user_from_request(request).id)

class TestCreateAndLoginUser(BaseTestCase):
    def test_logins_valid_user(self):
        user = self.factory.create_user(email='test@example.com')

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, user.name, user.email)
            login_user_mock.assert_called_once_with(user, remember=True)

    def test_creates_vaild_new_user(self):
        email = 'test@example.com'
        name = 'Test User'

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, name, email)

            self.assertTrue(login_user_mock.called)
            user = models.User.query.filter(models.User.email == email).one()
            self.assertEqual(user.email, email)

    def test_updates_user_name(self):
        user = self.factory.create_user(email='test@example.com')

        with patch('redash.authentication.google_oauth.login_user') as login_user_mock:
            create_and_login_user(self.factory.org, "New Name", user.email)
            login_user_mock.assert_called_once_with(user, remember=True)

class TestVerifyProfile(BaseTestCase):
    def test_no_domain_allowed_for_org(self):
        profile = dict(email='arik@example.com')
        self.assertFalse(verify_profile(self.factory.org, profile))

    def test_domain_not_in_org_domains_list(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org']
        self.assertFalse(verify_profile(self.factory.org, profile))

    def test_domain_in_org_domains_list(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.com']
        self.assertTrue(verify_profile(self.factory.org, profile))

        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org', 'example.com']
        self.assertTrue(verify_profile(self.factory.org, profile))

    def test_org_in_public_mode_accepts_any_domain(self):
        profile = dict(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_IS_PUBLIC] = True
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = []
        self.assertTrue(verify_profile(self.factory.org, profile))

    def test_user_not_in_domain_but_account_exists(self):
        profile = dict(email='arik@example.com')
        self.factory.create_user(email='arik@example.com')
        self.factory.org.settings[models.Organization.SETTING_GOOGLE_APPS_DOMAINS] = ['example.org']
        self.assertTrue(verify_profile(self.factory.org, profile))

class TestGetLoginUrl(BaseTestCase):
    def test_when_multi_org_enabled_and_org_exists(self):
        with self.app.test_request_context('/{}/'.format(self.factory.org.slug)):
            self.assertEqual(get_login_url(next=None), '/{}/login'.format(self.factory.org.slug))

    def test_when_multi_org_enabled_and_org_doesnt_exist(self):
        with self.app.test_request_context('/{}_notexists/'.format(self.factory.org.slug)):
            self.assertEqual(get_login_url(next=None), '/')

class TestRemoteUserAuth(BaseTestCase):
    DEFAULT_SETTING_OVERRIDES = {
        'REDASH_REMOTE_USER_LOGIN_ENABLED': 'true'
    }

    def setUp(self):
        # Apply default setting overrides to every test
        self.override_settings(None)

        super(TestRemoteUserAuth, self).setUp()

    def override_settings(self, overrides):
        """Override settings for testing purposes.

        This helper method can be used to override specific environmental
        variables to enable / disable Re:Dash features for the duration
        of the test.

        Note that these overrides only affect code that checks the value of
        the setting at runtime. It doesn't affect code that only checks the
        value during program initialization.

        :param dict overrides: a dict of environmental variables to override
            when the settings are reloaded
        """
        variables = self.DEFAULT_SETTING_OVERRIDES.copy()
        variables.update(overrides or {})
        with patch.dict(os.environ, variables):
            reload(settings)

        # Queue a cleanup routine that reloads the settings without overrides
        # once the test ends
        self.addCleanup(lambda: reload(settings))

    def assert_correct_user_attributes(self, user, email='test@example.com', name='test@example.com', groups=None, org=None):
        """Helper to assert that the user attributes are correct."""
        groups = groups or []
        if self.factory.org.default_group.id not in groups:
            groups.append(self.factory.org.default_group.id)

        self.assertIsNotNone(user)
        self.assertEqual(user.email, email)
        self.assertEqual(user.name, name)
        self.assertEqual(user.org, org or self.factory.org)
        self.assertItemsEqual(user.group_ids, groups)

    def get_test_user(self, email='test@example.com', org=None):
        """Helper to fetch an user from the database."""

        # Expire all cached objects to ensure these values are read directly
        # from the database.
        models.db.session.expire_all()

        return models.User.get_by_email_and_org(email, org or self.factory.org)

    def test_remote_login_disabled(self):
        self.override_settings({
            'REDASH_REMOTE_USER_LOGIN_ENABLED': 'false'
        })

        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Forwarded-Remote-User': 'test@example.com'
        })

        with self.assertRaises(NoResultFound):
            self.get_test_user()

    def test_remote_login_default_header(self):
        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Forwarded-Remote-User': 'test@example.com'
        })

        self.assert_correct_user_attributes(self.get_test_user())

    def test_remote_login_custom_header(self):
        self.override_settings({
            'REDASH_REMOTE_USER_HEADER': 'X-Custom-User'
        })

        self.get_request('/remote_user/login', org=self.factory.org, headers={
            'X-Custom-User': 'test@example.com'
        })

        self.assert_correct_user_attributes(self.get_test_user())
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Tests for aiohttp/client.py"""

import asyncio
import gc
import unittest
from unittest import mock
import sys

import aiohttp
from aiohttp.client import ClientSession
from aiohttp.multidict import MultiDict, CIMultiDict, CIMultiDictProxy
from aiohttp.connector import BaseConnector, TCPConnector
from aiohttp.client_reqrep import ClientRequest, ClientResponse
from http.cookies import SimpleCookie

PY_341 = sys.version_info >= (3, 4, 1)

class TestClientSession(unittest.TestCase):

    maxDiff = None

    def setUp(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(None)
        self.run = self.loop.run_until_complete

    def tearDown(self):
        self.loop.close()
        gc.collect()

    def make_open_connector(self):
        conn = BaseConnector(loop=self.loop)
        transp = unittest.mock.Mock()
        conn._conns['a'] = [(transp, 'proto', 123)]
        return conn

    def test_init_headers_simple_dict(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        self.assertEqual(
            sorted(session._default_headers.items()),
            ([("H1", "header1"),
              ("H2", "header2")]))
        session.close()

    def test_init_headers_list_of_tuples(self):
        session = ClientSession(
            headers=[("h1", "header1"),
                     ("h2", "header2"),
                     ("h3", "header3")],
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("h1", "header1"),
                         ("h2", "header2"),
                         ("h3", "header3")]))
        session.close()

    def test_init_headers_MultiDict(self):
        session = ClientSession(
            headers=MultiDict(
                [("h1", "header1"),
                 ("h2", "header2"),
                 ("h3", "header3")]),
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("H1", "header1"),
                         ("H2", "header2"),
                         ("H3", "header3")]))
        session.close()

    def test_init_headers_list_of_tuples_with_duplicates(self):
        session = ClientSession(
            headers=[("h1", "header11"),
                     ("h2", "header21"),
                     ("h1", "header12")],
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("H1", "header11"),
                         ("H2", "header21"),
                         ("H1", "header12")]))
        session.close()

    def test_init_cookies_with_simple_dict(self):
        session = ClientSession(
            cookies={
                "c1": "cookie1",
                "c2": "cookie2"
            }, loop=self.loop)
        self.assertEqual(set(session.cookies), {'c1', 'c2'})
        self.assertEqual(session.cookies['c1'].value, 'cookie1')
        self.assertEqual(session.cookies['c2'].value, 'cookie2')
        session.close()

    def test_init_cookies_with_list_of_tuples(self):
        session = ClientSession(
            cookies=[("c1", "cookie1"),
                     ("c2", "cookie2")],
            loop=self.loop)
        self.assertEqual(set(session.cookies), {'c1', 'c2'})
        self.assertEqual(session.cookies['c1'].value, 'cookie1')
        self.assertEqual(session.cookies['c2'].value, 'cookie2')
        session.close()

    def test_merge_headers(self):
        # Check incoming simple dict
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers({
            "h1": "h1"
        })
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def test_merge_headers_with_multi_dict(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers(MultiDict([("h1", "h1")]))
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def test_merge_headers_with_list_of_tuples(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers([("h1", "h1")])
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def _make_one(self, **kwargs):
        session = ClientSession(loop=self.loop, **kwargs)
        params = dict(
            headers={"Authorization": "Basic ..."},
            max_redirects=2,
            encoding="latin1",
            version=aiohttp.HttpVersion10,
            compress="deflate",
            chunked=True,
            expect100=True,
            read_until_eof=False)
        return session, params

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_GET(self, patched):
        session, params = self._make_one()
        self.run(session.get(
            "http://test.example.com",
            params={"x": 1},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("GET", "http://test.example.com",),
             dict(
                 params={"x": 1},
                 allow_redirects=True,
                 **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_OPTIONS(self, patched):
        session, params = self._make_one()
        self.run(session.options(
            "http://opt.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("OPTIONS", "http://opt.example.com",),
             dict(
                params={"x": 2},
                allow_redirects=True,
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_HEAD(self, patched):
        session, params = self._make_one()
        self.run(session.head(
            "http://head.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("HEAD", "http://head.example.com",),
             dict(
                params={"x": 2},
                allow_redirects=False,
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_POST(self, patched):
        session, params = self._make_one()
        self.run(session.post(
            "http://post.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("POST", "http://post.example.com",),
             dict(
                params={"x": 2},
                data="Some_data",
                files={"x": '1'},
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_PUT(self, patched):
        session, params = self._make_one()
        self.run(session.put(
            "http://put.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("PUT", "http://put.example.com",),
             dict(
                 params={"x": 2},
                 data="Some_data",
                 files={"x": '1'},
                 **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_PATCH(self, patched):
        session, params = self._make_one()
        self.run(session.patch(
            "http://patch.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("PATCH", "http://patch.example.com",),
             dict(
                params={"x": 2},
                data="Some_data",
                files={"x": '1'},
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_DELETE(self, patched):
        session, params = self._make_one()
        self.run(session.delete(
            "http://delete.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("DELETE", "http://delete.example.com",),
             dict(
                params={"x": 2},
                **params)])
        session.close()

    def test_close(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)

        session.close()
        self.assertIsNone(session.connector)
        self.assertTrue(conn.closed)

    def test_closed(self):
        session = ClientSession(loop=self.loop)
        self.assertFalse(session.closed)
        session.close()
        self.assertTrue(session.closed)

    def test_connector(self):
        connector = TCPConnector(loop=self.loop)
        session = ClientSession(connector=connector, loop=self.loop)
        self.assertIs(session.connector, connector)
        session.close()

    def test_connector_loop(self):
        loop = asyncio.new_event_loop()
        connector = TCPConnector(loop=loop)
        with self.assertRaisesRegex(
                ValueError,
                "loop argument must agree with connector"):
            ClientSession(connector=connector, loop=self.loop)
        connector.close()
        loop.close()

    def test_cookies_are_readonly(self):
        session = ClientSession(loop=self.loop)
        with self.assertRaises(AttributeError):
            session.cookies = 123
        session.close()

    def test_detach(self):
        session = ClientSession(loop=self.loop)
        conn = session.connector
        self.assertFalse(conn.closed)
        session.detach()
        self.assertIsNone(session.connector)
        self.assertTrue(session.closed)
        self.assertFalse(conn.closed)
        conn.close()

    def test_request_closed_session(self):
        @asyncio.coroutine
        def go():
            session = ClientSession(loop=self.loop)
            session.close()
            with self.assertRaises(RuntimeError):
                yield from session.request('get', '/')

        self.loop.run_until_complete(go())

    def test_close_flag_for_closed_connector(self):
        session = ClientSession(loop=self.loop)
        conn = session.connector
        self.assertFalse(session.closed)
        conn.close()
        self.assertTrue(session.closed)

    def test_double_close(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)

        session.close()
        self.assertIsNone(session.connector)
        session.close()
        self.assertTrue(session.closed)
        self.assertTrue(conn.closed)

    @unittest.skipUnless(PY_341, "Requires Python 3.4.1+")
    def test_del(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)
        self.loop.set_exception_handler(lambda loop, ctx: None)

        with self.assertWarns(ResourceWarning):
            del session
            gc.collect()

    def test_context_manager(self):
        conn = self.make_open_connector()
        with ClientSession(loop=self.loop, connector=conn) as session:
            pass

        self.assertTrue(session.closed)

    def test_borrow_connector_loop(self):
        conn = self.make_open_connector()
        session = ClientSession(connector=conn)
        self.assertIs(session._loop, self.loop)
        session.close()

class TestCLientRequest(unittest.TestCase):

    def setUp(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(None)
        self.connector = BaseConnector(loop=self.loop)
        self.transport = mock.Mock()
        self.protocol = mock.Mock()

    def tearDown(self):
        self.connector.close()
        self.loop.stop()
        self.loop.run_forever()
        self.loop.close()

    def test_custom_req_rep(self):
        @asyncio.coroutine
        def go():
            conn = None

            class CustomResponse(ClientResponse):
                @asyncio.coroutine
                def start(self, connection, read_until_eof=False):
                    nonlocal conn
                    conn = connection
                    self.status = 123
                    self.reason = 'Test OK'
                    self.headers = CIMultiDictProxy(CIMultiDict())
                    self.cookies = SimpleCookie()
                    return

            called = False

            class CustomRequest(ClientRequest):

                def send(self, writer, reader):
                    resp = self.response_class(self.method,
                                               self.url,
                                               self.host,
                                               writer=self._writer,
                                               continue100=self._continue)
                    resp._post_init(self.loop)
                    self.response = resp
                    nonlocal called
                    called = True
                    return resp

            @asyncio.coroutine
            def create_connection(req):
                self.assertIsInstance(req, CustomRequest)
                return self.transport, self.protocol
            self.connector._create_connection = create_connection

            resp = yield from aiohttp.request('get',
                                              'http://example.com/path/to',
                                              request_class=CustomRequest,
                                              response_class=CustomResponse,
                                              connector=self.connector,
                                              loop=self.loop)
            self.assertIsInstance(resp, CustomResponse)
            self.assertTrue(called)
            resp.close()
            conn.close()

        self.loop.run_until_complete(go())
<commit_msg>More tests<commit_after># -*- coding: utf-8 -*-
"""Tests for aiohttp/client.py"""

import asyncio
import gc
import unittest
from unittest import mock
import sys

import aiohttp
from aiohttp.client import ClientSession
from aiohttp.multidict import MultiDict, CIMultiDict, CIMultiDictProxy
from aiohttp.connector import BaseConnector, TCPConnector
from aiohttp.client_reqrep import ClientRequest, ClientResponse
from http.cookies import SimpleCookie

PY_341 = sys.version_info >= (3, 4, 1)

class TestClientSession(unittest.TestCase):

    maxDiff = None

    def setUp(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(None)
        self.run = self.loop.run_until_complete

    def tearDown(self):
        self.loop.close()
        gc.collect()

    def make_open_connector(self):
        conn = BaseConnector(loop=self.loop)
        transp = unittest.mock.Mock()
        conn._conns['a'] = [(transp, 'proto', 123)]
        return conn

    def test_init_headers_simple_dict(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        self.assertEqual(
            sorted(session._default_headers.items()),
            ([("H1", "header1"),
              ("H2", "header2")]))
        session.close()

    def test_init_headers_list_of_tuples(self):
        session = ClientSession(
            headers=[("h1", "header1"),
                     ("h2", "header2"),
                     ("h3", "header3")],
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("h1", "header1"),
                         ("h2", "header2"),
                         ("h3", "header3")]))
        session.close()

    def test_init_headers_MultiDict(self):
        session = ClientSession(
            headers=MultiDict(
                [("h1", "header1"),
                 ("h2", "header2"),
                 ("h3", "header3")]),
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("H1", "header1"),
                         ("H2", "header2"),
                         ("H3", "header3")]))
        session.close()

    def test_init_headers_list_of_tuples_with_duplicates(self):
        session = ClientSession(
            headers=[("h1", "header11"),
                     ("h2", "header21"),
                     ("h1", "header12")],
            loop=self.loop)
        self.assertEqual(
            session._default_headers,
            CIMultiDict([("H1", "header11"),
                         ("H2", "header21"),
                         ("H1", "header12")]))
        session.close()

    def test_init_cookies_with_simple_dict(self):
        session = ClientSession(
            cookies={
                "c1": "cookie1",
                "c2": "cookie2"
            }, loop=self.loop)
        self.assertEqual(set(session.cookies), {'c1', 'c2'})
        self.assertEqual(session.cookies['c1'].value, 'cookie1')
        self.assertEqual(session.cookies['c2'].value, 'cookie2')
        session.close()

    def test_init_cookies_with_list_of_tuples(self):
        session = ClientSession(
            cookies=[("c1", "cookie1"),
                     ("c2", "cookie2")],
            loop=self.loop)
        self.assertEqual(set(session.cookies), {'c1', 'c2'})
        self.assertEqual(session.cookies['c1'].value, 'cookie1')
        self.assertEqual(session.cookies['c2'].value, 'cookie2')
        session.close()

    def test_merge_headers(self):
        # Check incoming simple dict
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers({
            "h1": "h1"
        })
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def test_merge_headers_with_multi_dict(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers(MultiDict([("h1", "h1")]))
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def test_merge_headers_with_list_of_tuples(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers([("h1", "h1")])
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("h2", "header2"),
            ("h1", "h1")
        ]))
        session.close()

    def test_merge_headers_with_list_of_tuples_duplicated_names(self):
        session = ClientSession(
            headers={
                "h1": "header1",
                "h2": "header2"
            }, loop=self.loop)
        headers = session._prepare_headers([("h1", "v1"),
                                            ("h1", "v2")])
        self.assertIsInstance(headers, CIMultiDict)
        self.assertEqual(headers, CIMultiDict([
            ("H2", "header2"),
            ("H1", "v1"),
            ("H1", "v2"),
        ]))
        session.close()

    def _make_one(self, **kwargs):
        session = ClientSession(loop=self.loop, **kwargs)
        params = dict(
            headers={"Authorization": "Basic ..."},
            max_redirects=2,
            encoding="latin1",
            version=aiohttp.HttpVersion10,
            compress="deflate",
            chunked=True,
            expect100=True,
            read_until_eof=False)
        return session, params

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_GET(self, patched):
        session, params = self._make_one()
        self.run(session.get(
            "http://test.example.com",
            params={"x": 1},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("GET", "http://test.example.com",),
             dict(
                 params={"x": 1},
                 allow_redirects=True,
                 **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_OPTIONS(self, patched):
        session, params = self._make_one()
        self.run(session.options(
            "http://opt.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("OPTIONS", "http://opt.example.com",),
             dict(
                params={"x": 2},
                allow_redirects=True,
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_HEAD(self, patched):
        session, params = self._make_one()
        self.run(session.head(
            "http://head.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("HEAD", "http://head.example.com",),
             dict(
                params={"x": 2},
                allow_redirects=False,
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_POST(self, patched):
        session, params = self._make_one()
        self.run(session.post(
            "http://post.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("POST", "http://post.example.com",),
             dict(
                params={"x": 2},
                data="Some_data",
                files={"x": '1'},
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_PUT(self, patched):
        session, params = self._make_one()
        self.run(session.put(
            "http://put.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("PUT", "http://put.example.com",),
             dict(
                 params={"x": 2},
                 data="Some_data",
                 files={"x": '1'},
                 **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_PATCH(self, patched):
        session, params = self._make_one()
        self.run(session.patch(
            "http://patch.example.com",
            params={"x": 2},
            data="Some_data",
            files={"x": '1'},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("PATCH", "http://patch.example.com",),
             dict(
                params={"x": 2},
                data="Some_data",
                files={"x": '1'},
                **params)])
        session.close()

    @mock.patch("aiohttp.client.ClientSession.request")
    def test_http_DELETE(self, patched):
        session, params = self._make_one()
        self.run(session.delete(
            "http://delete.example.com",
            params={"x": 2},
            **params))
        self.assertTrue(patched.called, "`ClientSession.request` not called")
        self.assertEqual(
            list(patched.call_args),
            [("DELETE", "http://delete.example.com",),
             dict(
                params={"x": 2},
                **params)])
        session.close()

    def test_close(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)

        session.close()
        self.assertIsNone(session.connector)
        self.assertTrue(conn.closed)

    def test_closed(self):
        session = ClientSession(loop=self.loop)
        self.assertFalse(session.closed)
        session.close()
        self.assertTrue(session.closed)

    def test_connector(self):
        connector = TCPConnector(loop=self.loop)
        session = ClientSession(connector=connector, loop=self.loop)
        self.assertIs(session.connector, connector)
        session.close()

    def test_connector_loop(self):
        loop = asyncio.new_event_loop()
        connector = TCPConnector(loop=loop)
        with self.assertRaisesRegex(
                ValueError,
                "loop argument must agree with connector"):
            ClientSession(connector=connector, loop=self.loop)
        connector.close()
        loop.close()

    def test_cookies_are_readonly(self):
        session = ClientSession(loop=self.loop)
        with self.assertRaises(AttributeError):
            session.cookies = 123
        session.close()

    def test_detach(self):
        session = ClientSession(loop=self.loop)
        conn = session.connector
        self.assertFalse(conn.closed)
        session.detach()
        self.assertIsNone(session.connector)
        self.assertTrue(session.closed)
        self.assertFalse(conn.closed)
        conn.close()

    def test_request_closed_session(self):
        @asyncio.coroutine
        def go():
            session = ClientSession(loop=self.loop)
            session.close()
            with self.assertRaises(RuntimeError):
                yield from session.request('get', '/')

        self.loop.run_until_complete(go())

    def test_close_flag_for_closed_connector(self):
        session = ClientSession(loop=self.loop)
        conn = session.connector
        self.assertFalse(session.closed)
        conn.close()
        self.assertTrue(session.closed)

    def test_double_close(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)

        session.close()
        self.assertIsNone(session.connector)
        session.close()
        self.assertTrue(session.closed)
        self.assertTrue(conn.closed)

    @unittest.skipUnless(PY_341, "Requires Python 3.4.1+")
    def test_del(self):
        conn = self.make_open_connector()
        session = ClientSession(loop=self.loop, connector=conn)
        self.loop.set_exception_handler(lambda loop, ctx: None)

        with self.assertWarns(ResourceWarning):
            del session
            gc.collect()

    def test_context_manager(self):
        conn = self.make_open_connector()
        with ClientSession(loop=self.loop, connector=conn) as session:
            pass

        self.assertTrue(session.closed)

    def test_borrow_connector_loop(self):
        conn = self.make_open_connector()
        session = ClientSession(connector=conn)
        self.assertIs(session._loop, self.loop)
        session.close()

class TestCLientRequest(unittest.TestCase):

    def setUp(self):
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(None)
        self.connector = BaseConnector(loop=self.loop)
        self.transport = mock.Mock()
        self.protocol = mock.Mock()

    def tearDown(self):
        self.connector.close()
        self.loop.stop()
        self.loop.run_forever()
        self.loop.close()

    def test_custom_req_rep(self):
        @asyncio.coroutine
        def go():
            conn = None

            class CustomResponse(ClientResponse):
                @asyncio.coroutine
                def start(self, connection, read_until_eof=False):
                    nonlocal conn
                    conn = connection
                    self.status = 123
                    self.reason = 'Test OK'
                    self.headers = CIMultiDictProxy(CIMultiDict())
                    self.cookies = SimpleCookie()
                    return

            called = False

            class CustomRequest(ClientRequest):

                def send(self, writer, reader):
                    resp = self.response_class(self.method,
                                               self.url,
                                               self.host,
                                               writer=self._writer,
                                               continue100=self._continue)
                    resp._post_init(self.loop)
                    self.response = resp
                    nonlocal called
                    called = True
                    return resp

            @asyncio.coroutine
            def create_connection(req):
                self.assertIsInstance(req, CustomRequest)
                return self.transport, self.protocol
            self.connector._create_connection = create_connection

            resp = yield from aiohttp.request('get',
                                              'http://example.com/path/to',
                                              request_class=CustomRequest,
                                              response_class=CustomResponse,
                                              connector=self.connector,
                                              loop=self.loop)
            self.assertIsInstance(resp, CustomResponse)
            self.assertTrue(called)
            resp.close()
            conn.close()

        self.loop.run_until_complete(go())
<|endoftext|>


<commit_before>import os

from testcontainers.compose import DockerCompose

def test_can_spawn_service_via_compose():
    compose = DockerCompose(os.path.dirname(__file__))

    try:
        compose.start()
        host = compose.get_service_host("hub", 4444)
        port = compose.get_service_port("hub", 4444)
        assert host == "0.0.0.0"
        assert port == "4444"
    finally:
        compose.stop()
<commit_msg>docker-compose test added<commit_after>import os

import pytest

from testcontainers.compose import DockerCompose
from testcontainers.core.exceptions import NoSuchPortExposed

def test_can_spawn_service_via_compose():
    compose = DockerCompose(os.path.dirname(__file__))

    try:
        compose.start()
        host = compose.get_service_host("hub", 4444)
        port = compose.get_service_port("hub", 4444)
        assert host == "0.0.0.0"
        assert port == "4444"
    finally:
        compose.stop()

def test_can_throw_exception_if_no_port_exposed():
    compose = DockerCompose(os.path.dirname(__file__))

    compose.start()
    with pytest.raises(NoSuchPortExposed):
        compose.get_service_host("hub", 5555)

    compose.stop()
<|endoftext|>


<commit_before>import unittest

from flask import Flask
from flask_selfdoc import Autodoc

class TestErrorHandling(unittest.TestCase):
    def test_app_not_initialized(self):
        app = Flask(__name__)
        app.debug = True
        autodoc = Autodoc()
        with app.app_context():
            self.assertRaises(RuntimeError, lambda: autodoc.html())
<commit_msg>Turn off debug flag in test.<commit_after>import unittest

from flask import Flask
from flask_selfdoc import Autodoc

class TestErrorHandling(unittest.TestCase):
    def test_app_not_initialized(self):
        app = Flask(__name__)
        autodoc = Autodoc()
        with app.app_context():
            self.assertRaises(RuntimeError, lambda: autodoc.html())
<|endoftext|>


<commit_before>from collections import OrderedDict
from .fixture import AppTestCase

class TestExportRatings(AppTestCase):

    ORDER = 5

    def test_ratings_ad_redirect(self):
        resp = self.test_cli.get("/export_rating/csv/ad", allow_redirects=False)
        self.assertEqual(resp.status_code, 302)

        new_url = resp.headers["Location"]
        self.assertTrue(
            new_url.endswith("/export_rating/ad.csv")
        )

    def test_ratings_ad(self):
        resp = self.test_cli.get("/export_rating/ad.csv", allow_redirects=False)
        self.assertEqual(resp.status_code, 200)

        #with open(os.getcwd() + "/tests/samples/exported_ratings_ad.csv", 'w') as f:
        #    f.write(resp.text)
<commit_msg>Finished writing tests for export_rating<commit_after>from .fixture import AppTestCase

class TestExportRatings(AppTestCase):

    ORDER = 5

    def test_ratings_ad_redirect(self):
        resp = self.test_cli.get("/export_rating/csv/ad", allow_redirects=False)
        self.assertEqual(resp.status_code, 302)

        new_url = resp.headers["Location"]
        self.assertTrue(
            new_url.endswith("/export_rating/ad.csv")
        )

    def test_ratings_ad_csv(self):
        resp = self.test_cli.get("/export_rating/ad.csv", allow_redirects=False)
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.text, self.read_sample("exported_ratings_ad.csv"))

    def test_ratings_ad_json(self):
        resp = self.test_cli.get("/export_rating/ad.json", allow_redirects=False)
        self.assertEqual(resp.status_code, 200)
        self.assertDictEqual(
            resp.json(),
            self.read_json_sample("exported_ratings_ad")
        )

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    Pygments HTML formatter tests
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import io
import os
import re
import tempfile
from io import StringIO
from os import path

import pytest

from pygments.formatters import HtmlFormatter, NullFormatter
from pygments.formatters.html import escape_html
from pygments.lexers import PythonLexer
from pygments.style import Style

TESTDIR = path.dirname(path.abspath(__file__))
TESTFILE = path.join(TESTDIR, 'test_html_formatter.py')

with io.open(TESTFILE, encoding='utf-8') as fp:
    tokensource = list(PythonLexer().get_tokens(fp.read()))

def test_correct_output():
    hfmt = HtmlFormatter(nowrap=True)
    houtfile = StringIO()
    hfmt.format(tokensource, houtfile)

    nfmt = NullFormatter()
    noutfile = StringIO()
    nfmt.format(tokensource, noutfile)

    stripped_html = re.sub('<.*?>', '', houtfile.getvalue())
    escaped_text = escape_html(noutfile.getvalue())
    assert stripped_html == escaped_text

def test_external_css():
    # test correct behavior
    # CSS should be in /tmp directory
    fmt1 = HtmlFormatter(full=True, cssfile='fmt1.css', outencoding='utf-8')
    # CSS should be in TESTDIR (TESTDIR is absolute)
    fmt2 = HtmlFormatter(full=True, cssfile=path.join(TESTDIR, 'fmt2.css'),
                         outencoding='utf-8')
    tfile = tempfile.NamedTemporaryFile(suffix='.html')
    fmt1.format(tokensource, tfile)
    try:
        fmt2.format(tokensource, tfile)
        assert path.isfile(path.join(TESTDIR, 'fmt2.css'))
    except IOError:
        # test directory not writable
        pass
    tfile.close()

    assert path.isfile(path.join(path.dirname(tfile.name), 'fmt1.css'))
    os.unlink(path.join(path.dirname(tfile.name), 'fmt1.css'))
    try:
        os.unlink(path.join(TESTDIR, 'fmt2.css'))
    except OSError:
        pass

def test_all_options():
    def check(optdict):
        outfile = StringIO()
        fmt = HtmlFormatter(**optdict)
        fmt.format(tokensource, outfile)

    for optdict in [
        dict(nowrap=True),
        dict(linenos=True, full=True),
        dict(linenos=True, linespans='L'),
        dict(hl_lines=[1, 5, 10, 'xxx']),
        dict(hl_lines=[1, 5, 10], noclasses=True),
    ]:
        check(optdict)

    for linenos in [False, 'table', 'inline']:
        for noclasses in [False, True]:
            for linenospecial in [0, 5]:
                for anchorlinenos in [False, True]:
                    optdict = dict(
                        linenos=linenos,
                        noclasses=noclasses,
                        linenospecial=linenospecial,
                        anchorlinenos=anchorlinenos,
                    )
                    check(optdict)

def test_lineanchors():
    optdict = dict(lineanchors="foo")
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("
", html)

def test_lineanchors_with_startnum():
    optdict = dict(lineanchors="foo", linenostart=5)
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("
", html)

def test_valid_output():
    # test all available wrappers
    fmt = HtmlFormatter(full=True, linenos=True, noclasses=True,
                        outencoding='utf-8')

    handle, pathname = tempfile.mkstemp('.html')
    with os.fdopen(handle, 'w+b') as tfile:
        fmt.format(tokensource, tfile)
    catname = os.path.join(TESTDIR, 'dtds', 'HTML4.soc')
    try:
        import subprocess
        po = subprocess.Popen(['nsgmls', '-s', '-c', catname, pathname],
                              stdout=subprocess.PIPE)
        ret = po.wait()
        output = po.stdout.read()
        po.stdout.close()
    except OSError:
        # nsgmls not available
        pass
    else:
        if ret:
            print(output)
        assert not ret, 'nsgmls run reported errors'

    os.unlink(pathname)

def test_get_style_defs_contains_pre_style():
    style_defs = HtmlFormatter().get_style_defs().splitlines()
    assert style_defs[0] == 'pre { line-height: 125% }'

def test_get_style_defs_contains_default_line_numbers_styles():
    style_defs = HtmlFormatter().get_style_defs().splitlines()

    assert style_defs[1] == (
        'td.linenos pre '
        '{ color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[2] == (
        'span.linenos '
        '{ color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }'
    )

def test_get_style_defs_contains_style_specific_line_numbers_styles():
    class TestStyle(Style):
        line_number_color = '#ff0000'
        line_number_background_color = '#0000ff'
        line_number_special_color = '#00ff00'
        line_number_special_background_color = '#ffffff'

    style_defs = HtmlFormatter(style=TestStyle).get_style_defs().splitlines()

    assert style_defs[1] == (
        'td.linenos pre '
        '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[2] == (
        'span.linenos '
        '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[3] == (
        'td.linenos pre.special '
        '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[4] == (
        'span.linenos.special '
        '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
    )

@pytest.mark.parametrize(
    "formatter_kwargs, style_defs_args, assert_starts_with, assert_contains",
    [
        [{}, [], ".", []],
        [{"cssclass": "foo"}, [], ".foo .", []],
        [{"cssclass": "foo"}, [".bar"], ".bar .", []],
        [{"cssclass": "foo"}, [[".bar", ".baz"]], ".ba", [".bar .", ".baz ."]],
    ]
)
def test_get_token_style_defs_uses_css_prefix(
    formatter_kwargs, style_defs_args, assert_starts_with, assert_contains
):
    formatter = HtmlFormatter(**formatter_kwargs)

    for line in formatter.get_token_style_defs(*style_defs_args):
        assert line.startswith(assert_starts_with)
        for s in assert_contains:
            assert s in line

def test_get_background_style_defs_uses_multiple_css_prefixes():
    formatter = HtmlFormatter()

    lines = formatter.get_background_style_defs([".foo", ".bar"])
    assert lines[0].startswith(".foo .hll, .bar .hll {")
    assert lines[1].startswith(".foo , .bar {")

def test_unicode_options():
    fmt = HtmlFormatter(title='Föö',
                        cssclass='bär',
                        cssstyles='div:before { content: \'bäz\' }',
                        encoding='utf-8')
    handle, pathname = tempfile.mkstemp('.html')
    with os.fdopen(handle, 'w+b') as tfile:
        fmt.format(tokensource, tfile)

def test_ctags():
    try:
        import ctags
    except ImportError:
        # we can't check without the ctags module, but at least check the exception
        assert pytest.raises(
            RuntimeError, HtmlFormatter, tagsfile='support/tags'
        )
    else:
        # this tagfile says that test_ctags() is on line 165, even if it isn't
        # anymore in the actual source
        fmt = HtmlFormatter(tagsfile='support/tags', lineanchors='L',
                            tagurlformat='%(fname)s%(fext)s')
        outfile = StringIO()
        fmt.format(tokensource, outfile)
        assert 'test_ctags' \
            in outfile.getvalue()

def test_filename():
    optdict = dict(filename="test.py")
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("test.py
", html)
<commit_msg>Fix test.<commit_after># -*- coding: utf-8 -*-
"""
    Pygments HTML formatter tests
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import io
import os
import re
import tempfile
from io import StringIO
from os import path

import pytest

from pygments.formatters import HtmlFormatter, NullFormatter
from pygments.formatters.html import escape_html
from pygments.lexers import PythonLexer
from pygments.style import Style

TESTDIR = path.dirname(path.abspath(__file__))
TESTFILE = path.join(TESTDIR, 'test_html_formatter.py')

with io.open(TESTFILE, encoding='utf-8') as fp:
    tokensource = list(PythonLexer().get_tokens(fp.read()))

def test_correct_output():
    hfmt = HtmlFormatter(nowrap=True)
    houtfile = StringIO()
    hfmt.format(tokensource, houtfile)

    nfmt = NullFormatter()
    noutfile = StringIO()
    nfmt.format(tokensource, noutfile)

    stripped_html = re.sub('<.*?>', '', houtfile.getvalue())
    escaped_text = escape_html(noutfile.getvalue())
    assert stripped_html == escaped_text

def test_external_css():
    # test correct behavior
    # CSS should be in /tmp directory
    fmt1 = HtmlFormatter(full=True, cssfile='fmt1.css', outencoding='utf-8')
    # CSS should be in TESTDIR (TESTDIR is absolute)
    fmt2 = HtmlFormatter(full=True, cssfile=path.join(TESTDIR, 'fmt2.css'),
                         outencoding='utf-8')
    tfile = tempfile.NamedTemporaryFile(suffix='.html')
    fmt1.format(tokensource, tfile)
    try:
        fmt2.format(tokensource, tfile)
        assert path.isfile(path.join(TESTDIR, 'fmt2.css'))
    except IOError:
        # test directory not writable
        pass
    tfile.close()

    assert path.isfile(path.join(path.dirname(tfile.name), 'fmt1.css'))
    os.unlink(path.join(path.dirname(tfile.name), 'fmt1.css'))
    try:
        os.unlink(path.join(TESTDIR, 'fmt2.css'))
    except OSError:
        pass

def test_all_options():
    def check(optdict):
        outfile = StringIO()
        fmt = HtmlFormatter(**optdict)
        fmt.format(tokensource, outfile)

    for optdict in [
        dict(nowrap=True),
        dict(linenos=True, full=True),
        dict(linenos=True, linespans='L'),
        dict(hl_lines=[1, 5, 10, 'xxx']),
        dict(hl_lines=[1, 5, 10], noclasses=True),
    ]:
        check(optdict)

    for linenos in [False, 'table', 'inline']:
        for noclasses in [False, True]:
            for linenospecial in [0, 5]:
                for anchorlinenos in [False, True]:
                    optdict = dict(
                        linenos=linenos,
                        noclasses=noclasses,
                        linenospecial=linenospecial,
                        anchorlinenos=anchorlinenos,
                    )
                    check(optdict)

def test_lineanchors():
    optdict = dict(lineanchors="foo")
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("
", html)

def test_lineanchors_with_startnum():
    optdict = dict(lineanchors="foo", linenostart=5)
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("
", html)

def test_valid_output():
    # test all available wrappers
    fmt = HtmlFormatter(full=True, linenos=True, noclasses=True,
                        outencoding='utf-8')

    handle, pathname = tempfile.mkstemp('.html')
    with os.fdopen(handle, 'w+b') as tfile:
        fmt.format(tokensource, tfile)
    catname = os.path.join(TESTDIR, 'dtds', 'HTML4.soc')
    try:
        import subprocess
        po = subprocess.Popen(['nsgmls', '-s', '-c', catname, pathname],
                              stdout=subprocess.PIPE)
        ret = po.wait()
        output = po.stdout.read()
        po.stdout.close()
    except OSError:
        # nsgmls not available
        pass
    else:
        if ret:
            print(output)
        assert not ret, 'nsgmls run reported errors'

    os.unlink(pathname)

def test_get_style_defs_contains_pre_style():
    style_defs = HtmlFormatter().get_style_defs().splitlines()
    assert style_defs[0] == 'pre { line-height: 125%; }'

def test_get_style_defs_contains_default_line_numbers_styles():
    style_defs = HtmlFormatter().get_style_defs().splitlines()

    assert style_defs[1] == (
        'td.linenos pre '
        '{ color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[2] == (
        'span.linenos '
        '{ color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; }'
    )

def test_get_style_defs_contains_style_specific_line_numbers_styles():
    class TestStyle(Style):
        line_number_color = '#ff0000'
        line_number_background_color = '#0000ff'
        line_number_special_color = '#00ff00'
        line_number_special_background_color = '#ffffff'

    style_defs = HtmlFormatter(style=TestStyle).get_style_defs().splitlines()

    assert style_defs[1] == (
        'td.linenos pre '
        '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[2] == (
        'span.linenos '
        '{ color: #ff0000; background-color: #0000ff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[3] == (
        'td.linenos pre.special '
        '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
    )
    assert style_defs[4] == (
        'span.linenos.special '
        '{ color: #00ff00; background-color: #ffffff; padding-left: 5px; padding-right: 5px; }'
    )

@pytest.mark.parametrize(
    "formatter_kwargs, style_defs_args, assert_starts_with, assert_contains",
    [
        [{}, [], ".", []],
        [{"cssclass": "foo"}, [], ".foo .", []],
        [{"cssclass": "foo"}, [".bar"], ".bar .", []],
        [{"cssclass": "foo"}, [[".bar", ".baz"]], ".ba", [".bar .", ".baz ."]],
    ]
)
def test_get_token_style_defs_uses_css_prefix(
    formatter_kwargs, style_defs_args, assert_starts_with, assert_contains
):
    formatter = HtmlFormatter(**formatter_kwargs)

    for line in formatter.get_token_style_defs(*style_defs_args):
        assert line.startswith(assert_starts_with)
        for s in assert_contains:
            assert s in line

def test_get_background_style_defs_uses_multiple_css_prefixes():
    formatter = HtmlFormatter()

    lines = formatter.get_background_style_defs([".foo", ".bar"])
    assert lines[0].startswith(".foo .hll, .bar .hll {")
    assert lines[1].startswith(".foo , .bar {")

def test_unicode_options():
    fmt = HtmlFormatter(title='Föö',
                        cssclass='bär',
                        cssstyles='div:before { content: \'bäz\' }',
                        encoding='utf-8')
    handle, pathname = tempfile.mkstemp('.html')
    with os.fdopen(handle, 'w+b') as tfile:
        fmt.format(tokensource, tfile)

def test_ctags():
    try:
        import ctags
    except ImportError:
        # we can't check without the ctags module, but at least check the exception
        assert pytest.raises(
            RuntimeError, HtmlFormatter, tagsfile='support/tags'
        )
    else:
        # this tagfile says that test_ctags() is on line 165, even if it isn't
        # anymore in the actual source
        fmt = HtmlFormatter(tagsfile='support/tags', lineanchors='L',
                            tagurlformat='%(fname)s%(fext)s')
        outfile = StringIO()
        fmt.format(tokensource, outfile)
        assert 'test_ctags' \
            in outfile.getvalue()

def test_filename():
    optdict = dict(filename="test.py")
    outfile = StringIO()
    fmt = HtmlFormatter(**optdict)
    fmt.format(tokensource, outfile)
    html = outfile.getvalue()
    assert re.search("test.py
", html)
<|endoftext|>


<commit_before><commit_msg>Screw it, I'm deleting this entire test as not-very-useful<commit_after><|endoftext|>