devtools/_apptest.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4563 c25da7573ebd
parent 4982 4247066fd3de
child 5422 0865e1e90674
equal deleted inserted replaced
4563:c25da7573ebd 4985:02b52bf9f5f8
     1 """Hidden internals for the devtools.apptest module
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
     7 """
       
     8 __docformat__ = "restructuredtext en"
       
     9 
       
    10 import sys, traceback
       
    11 
       
    12 from logilab.common.pytest import pause_tracing, resume_tracing
       
    13 
       
    14 import yams.schema
       
    15 
       
    16 from cubicweb.dbapi import repo_connect, ConnectionProperties, ProgrammingError
       
    17 from cubicweb.cwvreg import CubicWebVRegistry
       
    18 
       
    19 from cubicweb.web.application import CubicWebPublisher
       
    20 from cubicweb.web import Redirect
       
    21 
       
    22 from cubicweb.devtools import ApptestConfiguration, init_test_database
       
    23 from cubicweb.devtools.fake import FakeRequest
       
    24 
       
    25 SYSTEM_ENTITIES = ('CWGroup', 'CWUser',
       
    26                    'CWAttribute', 'CWRelation',
       
    27                    'CWConstraint', 'CWConstraintType', 'CWProperty',
       
    28                    'CWEType', 'CWRType',
       
    29                    'Workflow', 'State', 'BaseTransition', 'Transition', 'WorkflowTransition', 'TrInfo', 'SubWorkflowExitPoint',
       
    30                    'RQLExpression',
       
    31                    )
       
    32 SYSTEM_RELATIONS = (
       
    33     # virtual relation
       
    34     'identity',
       
    35     # metadata
       
    36     'is', 'is_instance_of', 'owned_by', 'created_by', 'specializes',
       
    37     # workflow related
       
    38     'workflow_of', 'state_of', 'transition_of', 'initial_state', 'allowed_transition',
       
    39     'destination_state', 'in_state', 'wf_info_for', 'from_state', 'to_state',
       
    40     'condition', 'subworkflow', 'subworkflow_state', 'subworkflow_exit',
       
    41     # permission
       
    42     'in_group', 'require_group', 'require_permission',
       
    43     'read_permission', 'update_permission', 'delete_permission', 'add_permission',
       
    44     # eproperty
       
    45     'for_user',
       
    46     # schema definition
       
    47     'relation_type', 'from_entity', 'to_entity',
       
    48     'constrained_by', 'cstrtype', 'widget',
       
    49     # deducted from other relations
       
    50     'primary_email',
       
    51                     )
       
    52 
       
    53 def unprotected_entities(app_schema, strict=False):
       
    54     """returned a Set of each non final entity type, excluding CWGroup, and CWUser...
       
    55     """
       
    56     if strict:
       
    57         protected_entities = yams.schema.BASE_TYPES
       
    58     else:
       
    59         protected_entities = yams.schema.BASE_TYPES.union(set(SYSTEM_ENTITIES))
       
    60     entities = set(app_schema.entities())
       
    61     return entities - protected_entities
       
    62 
       
    63 
       
    64 def ignore_relations(*relations):
       
    65     global SYSTEM_RELATIONS
       
    66     SYSTEM_RELATIONS += relations
       
    67 
       
    68 
       
    69 class TestEnvironment(object):
       
    70     """TestEnvironment defines a context (e.g. a config + a given connection) in
       
    71     which the tests are executed
       
    72     """
       
    73 
       
    74     def __init__(self, appid, reporter=None, verbose=False,
       
    75                  configcls=ApptestConfiguration, requestcls=FakeRequest):
       
    76         config = configcls(appid)
       
    77         self.requestcls = requestcls
       
    78         self.cnx = None
       
    79         config.db_perms = False
       
    80         source = config.sources()['system']
       
    81         if verbose:
       
    82             print "init test database ..."
       
    83         self.vreg = vreg = CubicWebVRegistry(config)
       
    84         self.admlogin = source['db-user']
       
    85         # restore database <=> init database
       
    86         self.restore_database()
       
    87         if verbose:
       
    88             print "init done"
       
    89         config.repository = lambda x=None: self.repo
       
    90         self.app = CubicWebPublisher(config, vreg=vreg)
       
    91         self.verbose = verbose
       
    92         schema = self.vreg.schema
       
    93         # else we may run into problems since email address are ususally share in app tests
       
    94         # XXX should not be necessary anymore
       
    95         schema.rschema('primary_email').set_rproperty('CWUser', 'EmailAddress', 'composite', False)
       
    96         self.deletable_entities = unprotected_entities(schema)
       
    97 
       
    98     def restore_database(self):
       
    99         """called by unittests' tearDown to restore the original database
       
   100         """
       
   101         try:
       
   102             pause_tracing()
       
   103             if self.cnx:
       
   104                 self.cnx.close()
       
   105             source = self.vreg.config.sources()['system']
       
   106             self.repo, self.cnx = init_test_database(driver=source['db-driver'],
       
   107                                                      vreg=self.vreg)
       
   108             self._orig_cnx = self.cnx
       
   109             resume_tracing()
       
   110         except:
       
   111             resume_tracing()
       
   112             traceback.print_exc()
       
   113             sys.exit(1)
       
   114         # XXX cnx decoration is usually done by the repository authentication manager,
       
   115         # necessary in authentication tests
       
   116         self.cnx.vreg = self.vreg
       
   117         self.cnx.login = source['db-user']
       
   118         self.cnx.password = source['db-password']
       
   119 
       
   120 
       
   121     def create_user(self, login, groups=('users',), req=None):
       
   122         req = req or self.create_request()
       
   123         cursor = self._orig_cnx.cursor(req)
       
   124         rset = cursor.execute('INSERT CWUser X: X login %(login)s, X upassword %(passwd)s',
       
   125                               {'login': unicode(login), 'passwd': login.encode('utf8')})
       
   126         user = rset.get_entity(0, 0)
       
   127         cursor.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
       
   128                        % ','.join(repr(g) for g in groups),
       
   129                        {'x': user.eid}, 'x')
       
   130         user.clear_related_cache('in_group', 'subject')
       
   131         self._orig_cnx.commit()
       
   132         return user
       
   133 
       
   134     def login(self, login, password=None):
       
   135         if login == self.admlogin:
       
   136             self.restore_connection()
       
   137         else:
       
   138             self.cnx = repo_connect(self.repo, unicode(login),
       
   139                                     password or str(login),
       
   140                                     ConnectionProperties('inmemory'))
       
   141         if login == self.vreg.config.anonymous_user()[0]:
       
   142             self.cnx.anonymous_connection = True
       
   143         return self.cnx
       
   144 
       
   145     def restore_connection(self):
       
   146         if not self.cnx is self._orig_cnx:
       
   147             try:
       
   148                 self.cnx.close()
       
   149             except ProgrammingError:
       
   150                 pass # already closed
       
   151         self.cnx = self._orig_cnx
       
   152 
       
   153     ############################################################################
       
   154 
       
   155     def execute(self, rql, args=None, eidkey=None, req=None):
       
   156         """executes <rql>, builds a resultset, and returns a couple (rset, req)
       
   157         where req is a FakeRequest
       
   158         """
       
   159         req = req or self.create_request(rql=rql)
       
   160         return self.cnx.cursor(req).execute(unicode(rql), args, eidkey)
       
   161 
       
   162     def create_request(self, rql=None, **kwargs):
       
   163         """executes <rql>, builds a resultset, and returns a
       
   164         couple (rset, req) where req is a FakeRequest
       
   165         """
       
   166         if rql:
       
   167             kwargs['rql'] = rql
       
   168         req = self.requestcls(self.vreg, form=kwargs)
       
   169         req.set_connection(self.cnx)
       
   170         return req
       
   171 
       
   172     def get_rset_and_req(self, rql, optional_args=None, args=None, eidkey=None):
       
   173         """executes <rql>, builds a resultset, and returns a
       
   174         couple (rset, req) where req is a FakeRequest
       
   175         """
       
   176         return (self.execute(rql, args, eidkey),
       
   177                 self.create_request(rql=rql, **optional_args or {}))
       
   178 
       
   179 
       
   180 class ExistingTestEnvironment(TestEnvironment):
       
   181 
       
   182     def __init__(self, appid, sourcefile, verbose=False):
       
   183         config = ApptestConfiguration(appid, sourcefile=sourcefile)
       
   184         if verbose:
       
   185             print "init test database ..."
       
   186         source = config.sources()['system']
       
   187         self.vreg = CubicWebVRegistry(config)
       
   188         self.cnx = init_test_database(driver=source['db-driver'],
       
   189                                       vreg=self.vreg)[1]
       
   190         if verbose:
       
   191             print "init done"
       
   192         self.app = CubicWebPublisher(config, vreg=self.vreg)
       
   193         self.verbose = verbose
       
   194         # this is done when the publisher is opening a connection
       
   195         self.cnx.vreg = self.vreg
       
   196 
       
   197     def setup(self, config=None):
       
   198         """config is passed by TestSuite but is ignored in this environment"""
       
   199         cursor = self.cnx.cursor()
       
   200         self.last_eid = cursor.execute('Any X WHERE X creation_date D ORDERBY D DESC LIMIT 1').rows[0][0]
       
   201 
       
   202     def cleanup(self):
       
   203         """cancel inserted elements during tests"""
       
   204         cursor = self.cnx.cursor()
       
   205         cursor.execute('DELETE Any X WHERE X eid > %(x)s', {'x' : self.last_eid}, eid_key='x')
       
   206         print "cleaning done"
       
   207         self.cnx.commit()