Warning: Can't synchronize with repository "(default)" (Unsupported version control system "svn": No module named svn). Look in the Trac log for more information.

Ticket #1466: webtest_prelim.patch

File webtest_prelim.patch, 70.3 KB (added by kskuhlman, 4 years ago)

Preliminary effort for moving to webtest. Do not commit.

  • turbogears/controllers.py

     
    412412    assert isinstance(output, basestring) or isinstance(output, dict) \ 
    413413        or isinstance(output, types.GeneratorType), \ 
    414414           "Method %s.%s() returned unexpected output. Output should " \ 
    415            "be of type basestring, dict or generator." % ( 
    416             args[0].__class__.__name__, func.__name__) 
     415           "be of type basestring, dict or generator, not %s." % ( 
     416            args[0].__class__.__name__, func.__name__, type(output)) 
    417417    if isinstance(output, dict): 
    418418        template = output.pop("tg_template", template) 
    419419        format = output.pop("tg_format", format) 
  • turbogears/tests/test_form_controllers.py

     
    3434        self.name = name 
    3535        self.age = age 
    3636        self.date = date 
     37        return dict(name=name, age=age, date=date) 
    3738 
    3839    @expose() 
    3940    @validate(form=myform) 
     
    4344        self.name = name 
    4445        self.age = age 
    4546        self.date = date 
     47        return dict(name=name, age=age, date=date) 
    4648 
    4749def test_form_translation(): 
    4850    """Form input is translated into properly converted parameters""" 
    4951    root = MyRoot() 
    5052    cherrypy.root = root 
    51     testutil.create_request("/testform?name=ed&date=11/05/2005&age=5") 
     53    response = testutil.create_request("/testform?name=ed&date=11/05/2005&age=5") 
    5254    assert root.name == "ed" 
    5355    assert root.age == 5 
    5456 
     
    5658    """Form input is translated into properly converted parameters""" 
    5759    root = MyRoot() 
    5860    cherrypy.root = root 
    59     testutil.create_request("/testform_new_style?name=ed&date=11/05/2005&age=5&") 
     61    response = testutil.create_request("/testform_new_style?name=ed&date=11/05/2005&age=5&") 
    6062    assert root.name == "ed" 
    6163    assert root.age == 5 
    6264 
    6365def test_invalid_form_with_error_handling(): 
    6466    """Invalid forms can be handled by the method""" 
    65     root = cherrypy.root 
    66     testutil.create_request("/testform?name=ed&age=edalso&date=11/05/2005") 
    67     assert root.has_errors 
     67    response = testutil.create_request("/testform?name=ed&age=edalso&date=11/05/2005", status=200) 
    6868 
    6969def test_css_should_appear(): 
    7070    """CSS should appear when asked for""" 
    71     testutil.create_request("/") 
    72     assert "calendar-system.css" in cherrypy.response.body[0] 
     71    root = cherrypy.root 
     72    response = testutil.create_request("/") 
     73    print response.body 
     74    assert "calendar-system.css" in response.body 
    7375 
    7476def test_javascript_should_appear(): 
    7577    """JavaScript should appear when asked for""" 
    76     testutil.create_request("/") 
    77     assert "calendar.js" in cherrypy.response.body[0] 
     78    response = testutil.create_request("/") 
     79    assert "calendar.js" in response.body 
    7880 
    7981def test_include_mochikit(): 
    8082    """JSLinks (and MochiKit especially) can be included easily""" 
    81     testutil.create_request("/usemochi") 
    82     assert "MochiKit.js" in cherrypy.response.body[0] 
     83    response = testutil.create_request("/usemochi") 
     84    print response.body 
     85    assert "MochiKit.js" in response.body 
    8386 
    8487def test_suppress_mochikit(): 
    8588    """MochiKit inclusion can be suppressed""" 
    86     config.update({"global": {"tg.mochikit_suppress": True}}) 
    87     testutil.create_request("/usemochi") 
    88     suppressed_body = cherrypy.response.body[0] 
     89    config.update({"global":{"tg.mochikit_suppress" : True}}) 
     90    suppressed = testutil.create_request("/usemochi") 
    8991    # repair the fixture 
    90     config.update({"global": {"tg.mochikit_suppress": False}}) 
    91     testutil.create_request("/usemochi") 
    92     included_body = cherrypy.response.body[0] 
    93     assert "MochiKit.js" not in suppressed_body 
    94     assert "MochiKit.js" in included_body 
     92    config.update({"global":{"tg.mochikit_suppress" : False}}) 
    9593 
     94    included = testutil.create_request("/usemochi") 
     95    assert "MochiKit.js" not in suppressed.body 
     96    assert "MochiKit.js" in included.body 
     97 
    9698def test_mochikit_everywhere(): 
    9799    """MochiKit can be included everywhere by setting tg.mochikit_all""" 
    98     config.update({"global": {"tg.mochikit_all": True}}) 
    99     testutil.create_request("/") 
    100     config.update({"global": {"tg.mochikit_all": False}}) 
    101     assert "MochiKit.js" in cherrypy.response.body[0] 
     100    config.update({"global":{"tg.mochikit_all" : True}}) 
     101    response = testutil.create_request("/") 
     102    config.update({"global":{"tg.mochikit_all" : False}}) 
     103    assert "MochiKit.js" in response.body 
    102104 
    103105def test_mochikit_nowhere(): 
    104106    """Setting tg.mochikit_suppress will prevent including it everywhere""" 
    105107    config.update({"global": {"tg.mochikit_all": True}}) 
    106108    config.update({"global": {"tg.mochikit_suppress": True}}) 
    107     testutil.create_request("/") 
     109    response = testutil.create_request("/") 
    108110    config.update({"global": {"tg.mochikit_all": False}}) 
    109111    config.update({"global": {"tg.mochikit_suppress": False}}) 
    110     assert "MochiKit.js" not in cherrypy.response.body[0] 
     112    assert "MochiKit.js" not in response.body 
    111113 
    112114def test_include_widgets(): 
    113115    """Any widget can be included everywhere by setting tg.include_widgets""" 
    114116    config.update({"global": {"tg.include_widgets": ["mochikit"]}}) 
    115     testutil.create_request("/") 
     117    response = testutil.create_request("/") 
    116118    config.update({"global": {"tg.include_widgets": []}}) 
    117     assert "MochiKit.js" in cherrypy.response.body[0] 
     119    assert "MochiKit.js" in response.body 
    118120 
    119121 
    120122class State(object): 
     
    149151        cherrypy.root = self.Controller() 
    150152        # parameter values are irrelevant 
    151153        url = '/validation?a=1&b=2&c.a=3&c.b=4' 
    152         testutil.create_request(url) 
    153         body = cherrypy.response.body[0] 
     154        response = testutil.create_request(url) 
    154155        msg = "Validation state is not handled properly" 
    155156        # 4 == 1 (a) + 1(b) + 1(c.a) + 1(c.b) 
    156         self.failUnless('counter: 4' in body, msg) 
     157        self.failUnless('counter: 4' in response.body, msg) 
  • turbogears/tests/test_errorhandling.py

     
    223223 
    224224    def test_defaultErrorHandler(self): 
    225225        """ Default error handler. """ 
    226         testutil.create_request("/defaulterror?bar=abc") 
    227         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    228         testutil.create_request("/defaulterror?bar=true") 
    229         self.failUnless("Default error provider" in cherrypy.response.body[0]) 
     226        response = testutil.create_request("/defaulterror?bar=abc") 
     227        self.failUnless("Default error handler" in response.body) 
     228        response = testutil.create_request("/defaulterror?bar=true") 
     229        self.failUnless("Default error provider" in response.body) 
    230230 
    231231    def test_specialisedErrorHandler(self): 
    232232        """ Error handler specialisation. """ 
    233         testutil.create_request("/specialisederror?bar=abc&baz=a@b.com") 
    234         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    235         testutil.create_request("/specialisederror?baz=abc&bar=1") 
    236         self.failUnless("Specialised error handler" in 
    237                         cherrypy.response.body[0]) 
    238         testutil.create_request("/specialisederror?bar=1&baz=a@b.com") 
    239         self.failUnless("Specialised error provider" in 
    240                         cherrypy.response.body[0]) 
     233        response = testutil.create_request("/specialisederror?bar=abc&baz=a@b.com") 
     234        self.failUnless("Default error handler" in response.body) 
     235        response = testutil.create_request("/specialisederror?baz=abc&bar=1") 
     236        self.failUnless("Specialised error handler" in response.body) 
     237        response = testutil.create_request("/specialisederror?bar=1&baz=a@b.com") 
     238        self.failUnless("Specialised error provider" in response.body) 
    241239 
    242240    def test_exceptionErrorHandler(self): 
    243241        """ Error handler for exceptions. """ 
    244         testutil.create_request("/exceptionerror") 
    245         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
     242        response = testutil.create_request("/exceptionerror") 
     243        self.failUnless("Default error handler" in response.body) 
    246244 
    247245    def test_recursiveErrorHandler(self): 
    248246        """ Recursive error handler. """ 
    249         testutil.create_request("/recursiveerror?bar=abc") 
    250         self.failUnless("Recursive error handler" in cherrypy.response.body[0]) 
    251         testutil.create_request("/recursiveerror?bar=1") 
    252         self.failUnless("Recursive error provider" in 
    253                         cherrypy.response.body[0]) 
     247        response = testutil.create_request("/recursiveerror?bar=abc") 
     248        self.failUnless("Recursive error handler" in response.body) 
     249        response = testutil.create_request("/recursiveerror?bar=1") 
     250        self.failUnless("Recursive error provider" in response.body) 
    254251 
    255252    def test_implicitErrorHandler(self): 
    256253        """ Implicit error handling. """ 
    257         testutil.create_request("/impliciterror?bar=abc") 
    258         self.failUnless("Implicit error handler" in 
    259                         cherrypy.response.body[0]) 
    260         testutil.create_request("/impliciterror?bar=1") 
    261         self.failUnless("Implicit error provider" in 
    262                         cherrypy.response.body[0]) 
     254        response = testutil.create_request("/impliciterror?bar=abc") 
     255        self.failUnless("Implicit error handler" in response.body) 
     256        response = testutil.create_request("/impliciterror?bar=1") 
     257        self.failUnless("Implicit error provider" in response.body) 
    263258 
    264259    def test_normalMethodErrorHandler(self): 
    265260        """ Normal method as an error handler. """ 
    266         testutil.create_request("/normalmethodcaller?bar=abc") 
    267         self.failUnless("Normal method" in cherrypy.response.body[0]) 
    268         testutil.create_request("/normalmethodcaller?bar=true") 
    269         self.failUnless("Normal method caller" in cherrypy.response.body[0]) 
     261        response = testutil.create_request("/normalmethodcaller?bar=abc") 
     262        self.failUnless("Normal method" in response.body) 
     263        response = testutil.create_request("/normalmethodcaller?bar=true") 
     264        self.failUnless("Normal method caller" in response.body) 
    270265 
    271266    def test_infiniteRecursionPrevention(self): 
    272267        """ Infinite recursion prevention. """ 
    273         testutil.create_request("/infiniteloop") 
    274         self.failUnless("Exception 2" in cherrypy.response.body[0]) 
     268        response = testutil.create_request("/infiniteloop") 
     269        self.failUnless("Exception 2" in response.body) 
    275270 
    276271    def test_positionalArgs(self): 
    277272        """ Positional argument validation.  """ 
    278         testutil.create_request("/positionalargs/first/23/third?bar=abc") 
    279         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    280         testutil.create_request("/positionalargs/first/abc/third?bar=false") 
    281         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    282         testutil.create_request("/positionalargs/first/abc/third?bar=abc") 
    283         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    284         testutil.create_request("/positionalargs/first/23/third?bar=true") 
    285         self.failUnless("Positional arguments" in cherrypy.response.body[0]) 
     273        response = testutil.create_request("/positionalargs/first/23/third?bar=abc") 
     274        self.failUnless("Default error handler" in response.body) 
     275        response = testutil.create_request("/positionalargs/first/abc/third?bar=false") 
     276        self.failUnless("Default error handler" in response.body) 
     277        response = testutil.create_request("/positionalargs/first/abc/third?bar=abc") 
     278        self.failUnless("Default error handler" in response.body) 
     279        response = testutil.create_request("/positionalargs/first/23/third?bar=true") 
     280        self.failUnless("Positional arguments" in response.body) 
    286281        self.failUnless(cherrypy.root.first == "first") 
    287282        self.failUnless(cherrypy.root.second == 23) 
    288283        self.failUnless(cherrypy.root.third == "third") 
    289284 
    290285    def test_missingArgs(self): 
    291286        """ Arguments required in validation missing. """ 
    292         testutil.create_request("/missingargs") 
    293         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    294         testutil.create_request("/missingargs?bar=12") 
    295         self.failUnless("Missing args provider" in cherrypy.response.body[0]) 
     287        response = testutil.create_request("/missingargs") 
     288        self.failUnless("Default error handler" in response.body) 
     289        response = testutil.create_request("/missingargs?bar=12") 
     290        self.failUnless("Missing args provider" in response.body) 
    296291 
    297292    def test_nohandler(self): 
    298         """ No error hanlder declared. """ 
    299         testutil.create_request("/nohandler") 
    300         self.failUnless("Exception raised" in cherrypy.response.body[0]) 
     293        """ No error handler declared. """ 
     294        response = testutil.create_request("/nohandler") 
     295        self.failUnless("Exception raised" in response.body) 
    301296 
    302297    def test_bindArgs(self): 
    303298        """ Arguments can be bond to an error handler. """ 
    304         testutil.create_request("/bindargs") 
    305         self.failUnless("123" in cherrypy.response.body[0]) 
     299        response = testutil.create_request("/bindargs") 
     300        self.failUnless("123" in response.body) 
    306301 
    307302    def test_notExposed(self): 
    308303        """ Validation error handling is decoupled from expose. """ 
    309         testutil.create_request("/notexposedcaller?foo=a&bar=rab&baz=c") 
    310         self.failUnless("Not exposed error" in cherrypy.response.body[0]) 
    311         self.failUnless("rab" in cherrypy.response.body[0]) 
     304        response = testutil.create_request("/notexposedcaller?foo=a&bar=rab&baz=c") 
     305        self.failUnless("Not exposed error" in response.body) 
     306        self.failUnless("rab" in response.body) 
    312307 
    313308    def test_continuations(self): 
    314309        """ Continuations via error handling mechanism. """ 
    315         testutil.create_request("/continuationcaller?bar=a") 
    316         self.failUnless("Continuation caller" in cherrypy.response.body[0]) 
     310        response = testutil.create_request("/continuationcaller?bar=a") 
     311        self.failUnless("Continuation caller" in response.body) 
    317312        self.failUnless(cherrypy.root.continuation == True) 
    318313 
    319314    def test_nested(self): 
    320315        """ Potentially ambiguous cases. """ 
    321         testutil.create_request("/nest?bar=a") 
    322         self.failUnless("Default error handler" in cherrypy.response.body[0]) 
    323         testutil.create_request("/nestedcontroller/nest?bar=a") 
    324         self.failUnless("Nested" in cherrypy.response.body[0]) 
     316        response = testutil.create_request("/nest?bar=a") 
     317        self.failUnless("Default error handler" in response.body) 
     318        response = testutil.create_request("/nestedcontroller/nest?bar=a") 
     319        self.failUnless("Nested" in response.body) 
    325320 
    326321    def test_failsafe(self): 
    327322        """ Failsafe values for erroneous input. """ 
    328         testutil.create_request("/failsafenone?bar=a&baz=b") 
    329         self.failUnless('"bar": "a"' in cherrypy.response.body[0]) 
    330         self.failUnless('"baz": "b"' in cherrypy.response.body[0]) 
    331         testutil.create_request("/failsafevaluesdict?bar=a&baz=b") 
    332         self.failUnless('"bar": 1' in cherrypy.response.body[0]) 
    333         self.failUnless('"baz": 2' in cherrypy.response.body[0]) 
    334         testutil.create_request("/failsafevaluesatom?bar=a&baz=b") 
    335         self.failUnless('"bar": 13' in cherrypy.response.body[0]) 
    336         self.failUnless('"baz": 13' in cherrypy.response.body[0]) 
    337         testutil.create_request("/failsafemaperrors?bar=a&baz=b") 
     323        response = testutil.create_request("/failsafenone?bar=a&baz=b") 
     324        self.failUnless('"bar": "a"' in response.body) 
     325        self.failUnless('"baz": "b"' in response.body) 
     326        response = testutil.create_request("/failsafevaluesdict?bar=a&baz=b") 
     327        self.failUnless('"bar": 1' in response.body) 
     328        self.failUnless('"baz": 2' in response.body) 
     329        response = testutil.create_request("/failsafevaluesatom?bar=a&baz=b") 
     330        self.failUnless('"bar": 13' in response.body) 
     331        self.failUnless('"baz": 13' in response.body) 
     332        response = testutil.create_request("/failsafemaperrors?bar=a&baz=b") 
    338333        self.failUnless('"bar": "Please enter an integer value"' in 
    339                         cherrypy.response.body[0]) 
    340         self.failUnless('"baz": "Please enter an integer value"' in 
    341                         cherrypy.response.body[0]) 
    342         testutil.create_request("/failsafeformencode?bar=a&baz=b") 
    343         self.failUnless('"bar": 1' in cherrypy.response.body[0]) 
    344         self.failUnless('"baz": 2' in cherrypy.response.body[0]) 
    345         testutil.create_request("/failsafedefaults?bar=a&baz=b") 
    346         self.failUnless('"bar": 1' in cherrypy.response.body[0],cherrypy.response.body[0]) 
    347         self.failUnless('"baz": 2' in cherrypy.response.body[0]) 
     334                        response.body) 
     335        self.failUnless('"baz": "Please enter an integer value"' in  
     336                        response.body) 
     337        response = testutil.create_request("/failsafeformencode?bar=a&baz=b") 
     338        self.failUnless('"bar": 1' in response.body) 
     339        self.failUnless('"baz": 2' in response.body) 
     340        response = testutil.create_request("/failsafedefaults?bar=a&baz=b") 
     341        self.failUnless('"bar": 1' in response.body,response.body) 
     342        self.failUnless('"baz": 2' in response.body) 
  • turbogears/tests/test_sqlalchemy.py

     
    135135        will be marked with this name :) 
    136136 
    137137        """ 
    138         cherrypy.response.code = 501 
    139138        msg = "KARL25 responding\n" 
    140139        msg += "user with id: '%s' should not be saved.\n" % id 
    141140        msg += "An exception occurred: %r (%s)" % ((tg_exceptions,)*2) 
     
    181180    """If a controller causes an SA exception, it is raised properly.""" 
    182181    capture_log("turbogears.database") 
    183182    cherrypy.root = MyRoot() 
    184     create_request("/create_person?id=20") 
    185     output = cherrypy.response.body[0] 
     183    response = create_request("/create_person?id=20") 
    186184    print_log() 
     185    assert 'No exceptions occurred' in response.body 
     186    response = create_request("/create_person?id=20", status=500) 
     187    output = response.body 
    187188    print output 
    188     assert 'No exceptions occurred' in output 
    189     create_request("/create_person?id=20") 
    190     output = cherrypy.response.body[0] 
    191     print output 
    192189    assert 'KARL25' not in output, \ 
    193190        'Exception should NOT have been handled by our handler' 
    194191    assert 'DBAPIError' in output, \ 
     
    218215def test_cntrl_commit(): 
    219216    """It's safe to commit a transaction in the controller.""" 
    220217    cherrypy.root = MyRoot() 
    221     create_request("/create_person?id=23&docom=1") 
    222     assert 'InvalidRequestError' not in cherrypy.response.body[0] 
     218    response = create_request("/create_person?id=23&docom=1") 
     219    assert 'InvalidRequestError' not in response.body 
    223220    assert session.query(Person).get(23) is not None, \ 
    224221        'The Person 23 should have been saved during commit inside controller' 
    225222 
    226223def test_cntrl_commit2(): 
    227224    """A commit inside the controller is not rolled back by the exception.""" 
    228225    cherrypy.root = MyRoot() 
    229     create_request("/create_person?id=24&docom=1&doerr=1") 
    230     assert 'InvalidRequestError' not in cherrypy.response.body[0] 
     226    response = create_request("/create_person?id=24&docom=1&doerr=1") 
     227    assert 'InvalidRequestError' not in response.body 
    231228    assert session.query(Person).get(24) is not None, \ 
    232229        'The Person 24 should have been saved during commit' \ 
    233230        ' inside controller and not rolled back' 
     
    235232def test_cntrl_flush(): 
    236233    """It's safe to flush in the controller.""" 
    237234    cherrypy.root = MyRoot() 
    238     create_request("/create_person?id=25&doflush=1") 
    239     print cherrypy.response.body[0] 
    240     assert 'No exceptions occurred' in cherrypy.response.body[0] 
    241     create_request("/create_person?id=25&doflush=0") 
    242     assert 'IntegrityError' in cherrypy.response.body[0] 
    243     create_request("/create_person?id=25&doflush=1") 
    244     assert 'IntegrityError' in cherrypy.response.body[0] 
    245     create_request("/create_person?id=25&doflush=2") 
    246     assert 'No exceptions occurred' in cherrypy.response.body[0] 
     235    response = create_request("/create_person?id=25&doflush=1") 
     236    print response.body 
     237    assert 'No exceptions occurred' in response.body 
     238    response = create_request("/create_person?id=25&doflush=0", status=500) 
     239    assert 'IntegrityError' in response.body 
     240    response = create_request("/create_person?id=25&doflush=1") 
     241    assert 'IntegrityError' in response.body 
     242    response = create_request("/create_person?id=25&doflush=2") 
     243    assert 'No exceptions occurred' in response.body 
    247244 
    248245 
    249246# Exception handling with rollback 
     
    297294 
    298295    """ 
    299296    cherrypy.root = RbRoot() 
    300     create_request('/doerr?id=26') 
    301     output = cherrypy.response.body[0] 
     297    response = create_request('/doerr?id=26') 
     298    output = response.body 
    302299    print output 
    303300    assert 'KARL27' in output, 'Exception handler should have answered' 
    304301    assert session.query(User).get(26) is None 
     
    312309 
    313310    """ 
    314311    cherrypy.root = RbRoot() 
    315     create_request('/doerr?id=XX') 
    316     output = cherrypy.response.body[0] 
     312    response = create_request('/doerr?id=XX') 
     313    output = response.body 
    317314    assert 'KARL27' in output, 'Exception handler should have answered' 
    318315    assert session.query(User).get('XX') is None 
    319316 
    320317def test_exc_done_rollback(): 
    321318    """No problems with error handler if controller manually rollbacks.""" 
    322319    cherrypy.root = RbRoot() 
    323     create_request('/doerr?id=28&dorb=1') 
    324     output = cherrypy.response.body[0] 
     320    response = create_request('/doerr?id=28&dorb=1') 
     321    output = response.body 
    325322    assert 'KARL27' in output, 'Exception handler should have answered' 
    326323    assert session.query(User).get(28) is None 
    327324    assert session.query(User).get(29) is not None 
     
    356353    """ 
    357354    test_table.insert().execute(dict(id=1, val='a')) 
    358355    cherrypy.root = FreshRoot() 
    359     create_request("/test1") 
    360     assert 'AssertionError' not in cherrypy.response.body[0] 
     356    response = create_request("/test1", status=500) 
     357    assert 'AssertionError' not in response.body 
    361358    # Call test2 in a different thread 
    362359    class ThreadB(threading.Thread): 
    363360        def run(self): 
    364             create_request("/test2") 
    365             assert 'AssertionError' not in cherrypy.response.body[0] 
     361            response = create_request("/test2", status=500) 
     362            assert 'AssertionError' not in response.body 
    366363    thrdb = ThreadB() 
    367364    thrdb.start() 
    368365    thrdb.join() 
    369     create_request("/test3") 
    370     assert 'AssertionError' not in cherrypy.response.body[0] 
     366    response = create_request("/test3", status=500) 
     367    assert 'AssertionError' not in response.body 
  • turbogears/tests/test_expose.py

     
    2121 
    2222def test_gettinghtml(): 
    2323    cherrypy.root = ExposeRoot() 
    24     create_request("/with_json") 
    25     body = cherrypy.response.body[0] 
    26     assert "Paging all foo" in body 
     24    response = create_request("/with_json") 
     25    assert "Paging all foo" in response.body 
    2726 
    2827def test_gettingjson(): 
    2928    cherrypy.root = ExposeRoot() 
    30     create_request("/with_json?tg_format=json") 
    31     body = cherrypy.response.body[0] 
    32     assert '"title": "Foobar"' in body 
     29    response = create_request("/with_json?tg_format=json") 
     30    assert '"title": "Foobar"' in response.body 
    3331 
    3432def test_gettingjsonviaaccept(): 
    3533    cherrypy.root = ExposeRoot() 
    36     create_request("/with_json_via_accept", 
     34    response = create_request("/with_json_via_accept", 
    3735            headers=dict(Accept="text/javascript")) 
    38     body = cherrypy.response.body[0] 
    39     assert '"title": "Foobar"' in body 
     36    assert '"title": "Foobar"' in response.body 
    4037 
    4138def test_getting_json_with_accept_but_using_tg_format(): 
    4239    cherrypy.root = ExposeRoot() 
    43     create_request("/with_json_via_accept?tg_format=json") 
    44     body = cherrypy.response.body[0] 
    45     assert '"title": "Foobar"' in body 
     40    response = create_request("/with_json_via_accept?tg_format=json") 
     41    assert '"title": "Foobar"' in response.body 
    4642 
    4743def test_getting_plaintext(): 
    4844    cherrypy.root = ExposeRoot() 
    49     create_request("/with_json_via_accept", 
     45    response = create_request("/with_json_via_accept", 
    5046        headers=dict(Accept="text/plain")) 
    51     print cherrypy.response.body[0] 
    52     assert cherrypy.response.body[0] == "This is a plain text for foo." 
     47    assert response.body == "This is a plain text for foo." 
    5348 
    5449def test_allow_json(): 
    5550 
     
    5954            return dict(title="Foobar", mybool=False, someval="niggles") 
    6055 
    6156    cherrypy.root = NewRoot() 
    62     create_request("/test", headers= dict(accept="text/javascript")) 
    63     body = cherrypy.response.body[0] 
    64     values = simplejson.loads(body) 
     57    response = create_request("/test", headers= dict(accept="text/javascript")) 
     58    values = simplejson.loads(response.body) 
    6559    assert values == dict(title="Foobar", mybool=False, someval="niggles", 
    6660        tg_flash=None) 
    67     assert cherrypy.response.headers["Content-Type"] == "text/javascript" 
    68     create_request("/test?tg_format=json") 
    69     body = cherrypy.response.body[0] 
    70     values = simplejson.loads(body) 
     61    assert response.headers["Content-Type"] == "text/javascript" 
     62    response = create_request("/test?tg_format=json") 
     63    values = simplejson.loads(response.body) 
    7164    assert values == dict(title="Foobar", mybool=False, someval="niggles", 
    7265        tg_flash=None) 
    73     assert cherrypy.response.headers["Content-Type"] == "text/javascript" 
     66    assert response.headers["Content-Type"] == "text/javascript" 
  • turbogears/tests/test_controllers.py

     
    11import unittest 
     2import turbogears 
     3from turbogears import config, controllers, database, \ 
     4    error_handler, exception_handler, expose, flash, redirect, \ 
     5    startup, testutil, url, validate, validators 
     6import simplejson 
    27import formencode 
    38import cherrypy 
    49import pkg_resources 
    5 from turbogears import config, controllers, database, \ 
    6     error_handler, exception_handler, expose, flash, redirect, \ 
    7     startup, testutil, url, validate, validators 
     10from nose.tools import eq_ 
    811 
    912 
    1013class SubApp(controllers.RootController): 
    1114 
     15    @expose() 
    1216    def index(self): 
    1317        return url("/Foo/") 
    14     index = expose()(index) 
    1518 
     19    @expose() 
     20    def foo(self): 
     21        return url("foo") 
    1622 
     23    @expose() 
     24    def foo2(self): 
     25        return url("/foo") 
     26 
     27    @expose() 
     28    def redir(self): 
     29        turbogears.redirect("foo") 
     30 
     31    @expose() 
     32    def redir2(self): 
     33        raise turbogears.redirect("foo") 
     34 
     35 
    1736class MyRoot(controllers.RootController): 
    1837 
    1938    @expose() 
    2039    def index(self): 
    21         pass 
     40        return {} 
    2241 
    2342    def validation_error_handler(self, tg_source, tg_errors, *args, **kw): 
    2443        self.functionname = tg_source.__name__ 
     
    215234 
    216235    def test_js_files(self): 
    217236        """Can access the JavaScript files""" 
    218         testutil.create_request("/tg_js/MochiKit.js") 
    219         assert cherrypy.response.headers[ 
    220             "Content-Type"] == "application/x-javascript" 
    221         assert cherrypy.response.status == "200 OK" 
     237        response = testutil.create_request("/tg_js/MochiKit.js", status=200) 
     238        assert response.headers["Content-Type"] == "application/x-javascript" 
    222239 
    223240    def test_json_output(self): 
    224         testutil.create_request("/test?tg_format=json") 
    225         import simplejson 
    226         values = simplejson.loads(cherrypy.response.body[0]) 
    227         assert values == dict(title="Foobar", mybool=False, 
    228             someval="niggles", tg_flash=None) 
    229         assert cherrypy.response.headers["Content-Type"] == "text/javascript" 
     241        response = testutil.create_request("/test?tg_format=json") 
     242        values = simplejson.loads(response.body) 
     243        expected = dict(title="Foobar", mybool=False, someval="niggles", 
     244            tg_flash=None) 
     245        eq_(values, expected) 
     246        assert response.headers["Content-Type"] == "text/javascript" 
    230247 
    231248    def test_implied_json(self): 
    232         testutil.create_request("/impliedjson?tg_format=json") 
    233         assert '"title": "Blah"' in cherrypy.response.body[0] 
     249        response = testutil.create_request("/impliedjson?tg_format=json") 
     250        assert '"title": "Blah"' in response.body 
    234251 
    235252    def test_allow_json(self): 
    236         testutil.create_request("/allowjson?tg_format=json") 
    237         assert cherrypy.response.headers["Content-Type"] == "text/html" 
     253        response = testutil.create_request("/allowjson?tg_format=json", status=500) 
     254        assert response.headers["Content-Type"] == "text/html", response.headers 
    238255 
    239256    def test_allow_json_config(self): 
    240257        """JSON output can be enabled via config.""" 
     
    245262                return dict(title="Foobar", mybool=False, someval="foo", 
    246263                     tg_html="turbogears.tests.simple") 
    247264        cherrypy.root = JSONRoot() 
    248         testutil.create_request('/allowjsonconfig?tg_format=json') 
    249         assert cherrypy.response.headers["Content-Type"] == "text/javascript" 
    250         config.update({'tg.allow_json': False}) 
     265        response = testutil.create_request('/allowjsonconfig?tg_format=json') 
     266        assert response.headers["Content-Type"]=="text/javascript" 
     267        config.update({'tg.allow_json':False}) 
    251268 
    252269    def test_allow_json_config_false(self): 
    253270        """Make sure JSON can still be restricted with a global config on.""" 
     
    258275                return dict(title="Foobar", mybool=False, someval="foo", 
    259276                     tg_html="turbogears.tests.simple") 
    260277        cherrypy.root = JSONRoot() 
    261         testutil.create_request('/allowjson?tg_format=json') 
    262         assert cherrypy.response.headers["Content-Type"] == "text/html" 
     278        response = testutil.create_request('/allowjson?tg_format=json', status=404) 
     279        assert response.headers["Content-Type"]=="text/html" 
    263280        config.update({'tg.allow_json': False}) 
    264281 
    265282    def test_invalid_return(self): 
    266         testutil.create_request("/invalid") 
    267         assert cherrypy.response.status.startswith("500") 
     283        response = testutil.create_request("/invalid", status=500) 
    268284 
    269285    def test_strict_parameters(self): 
    270         config.update({"tg.strict_parameters": True}) 
    271         testutil.create_request( 
    272             "/save?submit=save&firstname=Foo&lastname=Bar&badparam=1") 
    273         assert cherrypy.response.status.startswith("500") 
    274         assert not hasattr(cherrypy.root, "errors") 
     286        config.update({"tg.strict_parameters" : True}) 
     287        response = testutil.create_request( 
     288            "/save?submit=save&firstname=Foo&lastname=Bar&badparam=1",  
     289            status = 500) 
    275290 
    276291    def test_throw_out_random(self): 
    277292        """Can append random value to the URL to avoid caching problems.""" 
    278         testutil.create_request("/test?tg_random=1") 
    279         assert "Paging all niggles" in cherrypy.response.body[0] 
     293        response = testutil.create_request("/test?tg_random=1") 
     294        assert "Paging all niggles" in response.body 
    280295        config.update({"tg.strict_parameters": True}) 
    281         testutil.create_request("/test?tg_random=1") 
    282         assert cherrypy.response.status.startswith("200") 
    283         assert "Paging all niggles" in cherrypy.response.body[0] 
    284         testutil.create_request("/test?tg_not_random=1") 
    285         assert cherrypy.response.status.startswith("500") 
    286         assert not hasattr(cherrypy.root, "errors") 
     296        response = testutil.create_request("/test?tg_random=1", status=200) 
     297        assert "Paging all niggles" in response.body 
     298        response = testutil.create_request("/test?tg_not_random=1", status=500) 
    287299 
    288300    def test_ignore_parameters(self): 
    289301        config.update({"tg.strict_parameters": True}) 
    290         testutil.create_request("/test?ignore_me=1") 
    291         assert cherrypy.response.status.startswith("500") 
     302        response = testutil.create_request("/test?ignore_me=1", status=500) 
    292303        assert not hasattr(cherrypy.root, "errors") 
    293304        config.update({"tg.ignore_parameters": ['ignore_me', 'me_too']}) 
    294         testutil.create_request("/test?ignore_me=1") 
    295         assert "Paging all niggles" in cherrypy.response.body[0] 
    296         testutil.create_request("/test?me_too=1") 
    297         assert cherrypy.response.status.startswith("200") 
    298         assert "Paging all niggles" in cherrypy.response.body[0] 
    299         testutil.create_request("/test?me_not=1") 
    300         assert cherrypy.response.status.startswith("500") 
     305        response = testutil.create_request("/test?ignore_me=1") 
     306        assert "Paging all niggles" in response.body 
     307        response = testutil.create_request("/test?me_too=1", status=200) 
     308        assert "Paging all niggles" in response.body 
     309        testutil.create_request("/test?me_not=1", status=500) 
    301310        assert not hasattr(cherrypy.root, "errors") 
    302311 
    303312    def test_retrieve_dict_directly(self): 
     
    305314        assert d["title"] == "Foobar" 
    306315 
    307316    def test_templateOutput(self): 
    308         testutil.create_request("/test") 
    309         assert "Paging all niggles" in cherrypy.response.body[0] 
     317        response = testutil.create_request("/test") 
     318        print response.body 
     319        assert "Paging all niggles" in response.body 
    310320 
     321    def test_throw_out_random(self): 
     322        """A random value can be appended to the URL to avoid caching 
     323        problems.""" 
     324        response = testutil.create_request("/test?tg_random=1") 
     325        assert "Paging all niggles" in response.body 
     326 
    311327    def test_safari_unicode_fix(self): 
    312         testutil.create_request("/unicode", headers={'User-Agent': 
    313             "Apple WebKit Safari/412.2"}) 
    314         firstline = cherrypy.response.body[0].split('\n')[0] 
     328        response = testutil.create_request("/unicode", headers={'User-Agent' : 
     329            "Apple WebKit Safari/412.2"}, status=200) 
     330        firstline = response.body.split('\n')[0] 
    315331        assert firstline == "¿Habla español?" 
    316332        assert isinstance(firstline, str) 
    317333 
    318334    def test_default_format(self): 
    319335        """The default format can be set via expose""" 
    320         testutil.create_request("/returnjson") 
    321         firstline = cherrypy.response.body[0] 
    322         assert '"title": "Foobar"' in firstline 
    323         testutil.create_request("/returnjson?tg_format=html") 
    324         firstline = cherrypy.response.body[0] 
    325         assert '"title": "Foobar"' not in firstline 
     336        response = testutil.create_request("/returnjson") 
     337        assert '"title": "Foobar"' in response.body 
     338        response = testutil.create_request("/returnjson?tg_format=html", status=500) 
     339        assert '"title": "Foobar"' not in response.body 
    326340 
    327341    def test_content_type(self): 
    328342        """The content-type can be set via expose""" 
    329         testutil.create_request("/contenttype") 
    330         assert cherrypy.response.headers["Content-Type"] == "xml/atom" 
     343        response = testutil.create_request("/contenttype") 
     344        assert response.headers["Content-Type"] == "xml/atom" 
    331345 
    332346    def test_returned_template_name(self): 
    333         testutil.create_request("/returnedtemplate") 
    334         data = cherrypy.response.body[0].lower() 
     347        response = testutil.create_request("/returnedtemplate") 
     348        data = response.body.lower() 
    335349        assert "<body>" in data 
    336350        assert 'groovy test template' in data 
    337351 
    338352    def test_returned_template_short(self): 
    339         testutil.create_request("/returnedtemplate_short") 
    340         assert "Paging all foo" in cherrypy.response.body[0] 
     353        response = testutil.create_request("/returnedtemplate_short") 
     354        assert "Paging all foo" in response.body 
    341355 
    342356    def test_expose_template_short(self): 
    343         testutil.create_request("/exposetemplate_short") 
    344         assert "Paging all foo" in cherrypy.response.body[0] 
     357        response = testutil.create_request("/exposetemplate_short") 
     358        assert "Paging all foo" in response.body 
    345359 
    346360    def test_validation(self): 
    347361        """Data can be converted and validated""" 
    348         testutil.create_request("/istrue?value=true") 
     362        #TODO: Convert these to testutil.call, to make them less dependant on cp internals 
     363        response = testutil.create_request("/istrue?value=true") 
    349364        assert cherrypy.root.value is True 
    350365        testutil.create_request("/istrue?value=false") 
    351366        assert cherrypy.root.value is False 
    352367        cherrypy.root = MyRoot() 
    353         testutil.create_request("/istrue?value=foo") 
     368        response = testutil.create_request("/istrue?value=foo") 
    354369        assert not hasattr(cherrypy.root, "value") 
    355370        assert cherrypy.root.functionname == "istrue" 
    356         testutil.create_request("/save?submit=send&firstname=John&lastname=Doe") 
     371 
     372        response = testutil.create_request("/save?submit=send&firstname=John&lastname=Doe") 
    357373        assert cherrypy.root.fullname == "John Doe" 
    358374        assert cherrypy.root.submit == "send" 
    359375        testutil.create_request("/save?submit=send&firstname=Arthur") 
     
    369385        assert cherrypy.root.errors.has_key("firstname") 
    370386 
    371387    def test_validation_with_schema(self): 
    372         """Data can be converted/validated with formencode.Schema instance""" 
     388        """Data can be converted and validated with formencode.Schema instance""" 
    373389        testutil.create_request("/save2?submit=send&firstname=Joe&lastname=Doe") 
    374390        assert cherrypy.root.fullname == "Joe Doe" 
    375391        assert cherrypy.root.submit == "send" 
     
    384400 
    385401    def test_other_template(self): 
    386402        """'tg_html' in a returned dict will use the template specified there""" 
    387         testutil.create_request("/useother") 
    388         assert "This is the other template" in cherrypy.response.body[0] 
     403        response = testutil.create_request("/useother") 
     404        assert "This is the other template" in response.body, response.body 
    389405 
    390406    def test_cheetah_template(self): 
    391407        """Cheetah templates can be used as well""" 
    392         testutil.create_request("/usecheetah") 
    393         body = cherrypy.response.body[0] 
    394         assert "This is the Cheetah test template." in body 
    395         assert "Paging all chimps." in body 
     408        response = testutil.create_request("/usecheetah") 
     409        assert "This is the Cheetah test template." in response.body 
     410        assert "Paging all chimps." in response.body 
    396411 
    397412    def test_run_with_trans(self): 
    398413        """run_with_transaction is called only on topmost exposed method""" 
     
    405420 
    406421    def test_positional(self): 
    407422        """Positional parameters should work""" 
    408         testutil.create_request("/pos/foo") 
     423        response = testutil.create_request("/pos/foo") 
    409424        assert cherrypy.root.posvalue == "foo" 
    410425 
    411426    def test_flash_plain(self): 
    412427        """flash with strings should work""" 
    413         testutil.create_request("/flash_plain?tg_format=json") 
    414         import simplejson 
    415         values = simplejson.loads(cherrypy.response.body[0]) 
     428        response = testutil.create_request("/flash_plain?tg_format=json") 
     429        values = simplejson.loads(response.body) 
    416430        assert values["tg_flash"] == "plain" 
    417         assert not cherrypy.response.simple_cookie.has_key("tg_flash") 
     431        assert not response.cookies_set.has_key("tg_flash"), response.cookies_set 
    418432 
    419433    def test_flash_unicode(self): 
    420434        """flash with unicode objects should work""" 
    421         testutil.create_request("/flash_unicode?tg_format=json") 
    422         import simplejson 
    423         values = simplejson.loads(cherrypy.response.body[0]) 
     435        response = testutil.create_request("/flash_unicode?tg_format=json") 
     436        values = simplejson.loads(response.body) 
    424437        assert values["tg_flash"] == u"\xfcnicode" 
    425         assert not cherrypy.response.simple_cookie.has_key("tg_flash") 
     438        assert not response.cookies_set.has_key("tg_flash"), response.cookies_set 
    426439 
    427440    def test_flash_on_redirect(self): 
    428441        """flash must survive a redirect""" 
    429         testutil.create_request("/flash_redirect?tg_format=json") 
    430         assert cherrypy.response.status.startswith("302") 
    431         testutil.create_request(cherrypy.response.headers["Location"], 
    432             headers=dict(Cookie 
    433                 =cherrypy.response.simple_cookie.output(header="").strip())) 
    434         import simplejson 
    435         values = simplejson.loads(cherrypy.response.body[0]) 
     442        response = testutil.create_request("/flash_redirect?tg_format=json", 
     443            status = 302) 
     444        response = testutil.create_request(response.headers["Location"],  
     445            headers=dict(Cookie=response.cookies_set.output())) 
     446        print response.body 
     447        values = simplejson.loads(response.body) 
    436448        assert values["tg_flash"] == u"redirect \xfcnicode" 
    437449 
    438450    def test_double_flash(self): 
     
    440452        # Here we are calling method that sets a flash message. However flash 
    441453        # cookie is still there. Turbogears should discard old flash message 
    442454        # from cookie and use new one, set by flash_plain(). 
    443         testutil.create_request("/flash_plain?tg_format=json", 
    444             headers=dict(Cookie='tg_flash="old flash"; Path=/;')) 
    445         import simplejson 
    446         values = simplejson.loads(cherrypy.response.body[0]) 
     455        response = testutil.create_request("/flash_plain?tg_format=json", 
     456            headers= {'Cookie':'tg_flash="old flash"; Path=/;'}) 
     457        values = simplejson.loads(response.body) 
    447458        assert values["tg_flash"] == "plain" 
    448         assert cherrypy.response.simple_cookie.has_key("tg_flash"), \ 
     459        assert response.cookies_set.has_key("tg_flash"), \ 
    449460                "Cookie clearing request should be present" 
    450         flashcookie = cherrypy.response.simple_cookie['tg_flash'] 
     461        flashcookie = response.cookies_set['tg_flash'] 
    451462        assert flashcookie['expires'] == 0 
    452463 
    453464    def test_set_kid_outputformat_in_config(self): 
    454465        """the outputformat for kid can be set in the config""" 
    455466        config.update({'kid.outputformat': 'xhtml'}) 
    456         testutil.create_request('/test') 
    457         response = cherrypy.response.body[0] 
    458         assert '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ' in response 
     467        response = testutil.create_request('/test') 
     468        assert '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ' in response.body 
    459469        config.update({'kid.outputformat': 'html'}) 
    460         testutil.create_request('/test') 
    461         response = cherrypy.response.body[0] 
    462         assert  '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ' in response 
    463         assert '    This is the groovy test ' in response 
     470        response = testutil.create_request('/test') 
     471        assert  '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ' in response.body 
     472        assert '    This is the groovy test ' in response.body 
    464473        config.update({'kid.outputformat': 'html compact'}) 
    465         testutil.create_request('/test') 
    466         response = cherrypy.response.body[0] 
    467         assert  '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ' in response 
    468         assert 'This is the groovy test ' in response 
    469         assert '    ' not in response 
     474        response = testutil.create_request('/test') 
     475        assert  '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ' in response.body 
     476        assert 'This is the groovy test ' in response.body 
     477        assert '    ' not in response.body 
    470478 
    471479    def test_fileserving(self): 
    472         #outputcap = StringIO() 
    473         #sys.stdout = outputcap 
    474480        testutil.create_request("/servefile") 
    475481        assert cherrypy.root.servedit 
    476482        assert not cherrypy.root.serve_exceptions 
     
    478484 
    479485    def test_internal_redirect(self): 
    480486        """regression test for #1022, #1407 and #1598""" 
    481         testutil.create_request("/internal_redirect") 
    482         firstline = cherrypy.response.body[0] 
    483         assert "redirected OK" in firstline 
     487        response = testutil.create_request("/internal_redirect") 
     488        assert "redirected OK" in response.body 
    484489 
    485490    def test_internal_redirect_nested_variables(self): 
    486491        """regression test for #1022, #1407 and #1598""" 
    487         testutil.create_request( 
     492        response = testutil.create_request( 
    488493            "/internal_redirect?a=1&a-1.b=2&a-2.c=3&a-2.c-1=4") 
    489         firstline = cherrypy.response.body[0] 
    490         assert "redirected OK" in firstline 
     494        assert "redirected OK" in response.body 
    491495 
    492496    def test_exc_value(self): 
    493497        """Exception is handled gracefully by the right exception handler.""" 
    494         testutil.create_request("/raise_value_exc") 
    495         assert 'handling_value' in cherrypy.response.body[0] 
     498        response = testutil.create_request("/raise_value_exc") 
     499        print response.body 
     500        assert 'handling_value' in response.body 
    496501 
    497502    def test_exc_index(self): 
    498503        """Exception is handled gracefully by the right exception handler.""" 
    499         testutil.create_request("/raise_index_exc") 
    500         assert 'handling_index' in cherrypy.response.body[0] 
     504        response = testutil.create_request("/raise_index_exc") 
     505        assert 'handling_index' in response.body, response.body 
    501506 
    502507    def test_exc_all(self): 
    503508        """Test a controller that is protected by multiple exception handlers. 
     
    506511        by their respective handlers without problem... 
    507512 
    508513        """ 
    509         testutil.create_request("/raise_all_exc?num=1") 
    510         assert 'handling_value' in cherrypy.response.body[0] 
    511         testutil.create_request("/raise_all_exc?num=2") 
    512         assert 'handling_index' in cherrypy.response.body[0] 
    513         testutil.create_request("/raise_all_exc?num=3") 
    514         assert 'handling_key' in cherrypy.response.body[0] 
     514        response = testutil.create_request("/raise_all_exc?num=1") 
     515        assert 'handling_value' in response.body, response.body 
     516        response = testutil.create_request("/raise_all_exc?num=2") 
     517        assert 'handling_index' in response.body, response.body 
     518        response = testutil.create_request("/raise_all_exc?num=3") 
     519        assert 'handling_key' in response.body, response.body 
    515520 
    516521 
    517522class TestURLs(unittest.TestCase): 
     
    521526        cherrypy.root = MyRoot() 
    522527        cherrypy.root.subthing = SubApp() 
    523528        cherrypy.root.subthing.subsubthing = SubApp() 
     529         
     530        if testutil.harness == 'webob': 
     531            self.location_base = 'http://localhost:80' 
     532        else: 
     533            self.location_base = 'http://localhost' 
    524534 
    525535    def test_basic_urls(self): 
    526536        testutil.create_request("/") 
     
    537547        assert url("/foo") == "/foo" 
    538548 
    539549    def test_approots(self): 
    540         testutil.create_request("/subthing/") 
    541         assert url("foo") == "foo" 
    542         assert url("/foo") == "/subthing/foo" 
     550        response = testutil.create_request("/subthing/foo") 
     551        self.failUnlessEqual("foo", response.body) 
     552           
     553        response = testutil.create_request("/subthing/foo2") 
     554        self.failUnlessEqual("/subthing/foo", response.body) 
    543555 
    544556    def test_lower_approots(self): 
    545         testutil.create_request("/subthing/subsubthing/") 
    546         assert url("/foo") == "/subthing/subsubthing/foo" 
     557        config.update({"server.webpath" : "/XXX"}) 
     558        response = testutil.create_request("/subthing/subsubthing/") 
     559        eq_("/XXX/subthing/subsubthing/Foo/", response.body) 
    547560 
    548     def test_approots_With_path(self): 
    549         config.update({"server.webpath": "/coolsite/root"}) 
     561    def test_approots_with_path(self): 
     562        config.update({"server.webpath" : "/coolsite/root"}) 
    550563        startup.startTurboGears() 
    551         testutil.create_request("/coolsite/root/subthing/") 
    552         assert url("/foo") == "/coolsite/root/subthing/foo" 
     564        response = testutil.create_request("/coolsite/root/subthing/") 
     565        self.failUnlessEqual("/coolsite/root/subthing/Foo/", response.body) 
    553566 
    554567    def test_redirect(self): 
    555         config.update({"server.webpath": "/coolsite/root"}) 
     568        config.update({"server.webpath" : "/coolsite/root"}) 
    556569        startup.startTurboGears() 
    557         testutil.create_request("/coolsite/root/subthing/") 
    558         try: 
    559             redirect("/foo") 
    560             assert False, "redirect exception should have been raised" 
    561         except cherrypy.HTTPRedirect, e: 
    562             assert "http://localhost/coolsite/root/subthing/foo" in e.urls 
    563         try: 
    564             raise redirect("/foo") 
    565             assert False, "redirect exception should have been raised" 
    566         except cherrypy.HTTPRedirect, e: 
    567             assert "http://localhost/coolsite/root/subthing/foo" in e.urls 
     570        response = testutil.create_request("/coolsite/root/subthing/redir") 
     571        self.failUnlessEqual(response.location, self.location_base  + '/coolsite/root/subthing/foo') 
    568572 
     573        response = testutil.create_request("/coolsite/root/subthing/redir2") 
     574        self.failUnlessEqual(response.location, self.location_base + '/coolsite/root/subthing/foo') 
     575 
    569576    def test_multi_values(self): 
    570577        testutil.create_request("/") 
    571578        assert url("/foo", bar=[1, 2]) in \ 
     
    593600        config.update({"server.webpath": ""}) 
    594601        startup.startTurboGears() 
    595602 
    596  
    597603def test_index_trailing_slash(): 
    598604    """If there is no trailing slash on an index method call, redirect""" 
    599605    cherrypy.root = SubApp() 
    600606    cherrypy.root.foo = SubApp() 
    601     testutil.create_request("/foo") 
    602     assert cherrypy.response.status.startswith("302") 
     607    response  = testutil.create_request("/foo") 
     608    print response.status 
     609    assert response.status.startswith("302") 
    603610 
     611 
    604612def test_can_use_internally_defined_arguments(): 
    605613    """Can use argument names that are internally used by TG in controllers""" 
    606614 
     
    611619            return "\n".join(["%s:%s" % i for i in kw.iteritems()]) 
    612620 
    613621    cherrypy.root = App() 
    614     testutil.create_request("/?format=foo&template=bar&fragment=boo") 
    615     output = cherrypy.response.body[0] 
     622    response = testutil.create_request("/?format=foo&template=bar&fragment=boo") 
     623    output = response.body 
    616624    assert "format:foo" in output 
    617625    assert "template:bar" in output 
    618626    assert "fragment:boo" in output 
  • turbogears/tests/test_view.py

     
    11# -*- coding: utf-8 -*- 
    2  
    3 from turbogears import view, config 
     2import cherrypy 
     3from turbogears import view, config, startup, controllers, expose, testutil 
    44import unittest 
    55 
     6class MyRoot(controllers.RootController): 
     7    def index(self): 
     8        return {} 
     9    index = expose()(index) 
     10 
    611class TestView(unittest.TestCase): 
    712 
     13    def setUp(self): 
     14        cherrypy.root = MyRoot() 
     15        startup.startTurboGears() 
     16 
    817    def test_UnicodeValueAppearingInATemplateIsFine(self): 
     18        testutil.create_request('/') 
    919        ustr = u"micro-eXtreme Programming ( µ XP): Embedding XP Within Standard Projects" 
    1020        info = dict(someval=ustr) 
    1121        val = view.render(info, template="turbogears.tests.simple") 
    1222        self.failUnless(u"Paging all " + ustr in val.decode("utf-8")) 
    1323 
    1424    def test_templateRetrievalByPath(self): 
     25        testutil.create_request('/') 
    1526        config.update({'server.environment' : 'development'}) 
    1627        from turbokid import kidsupport 
    1728        ks = kidsupport.KidSupport() 
  • turbogears/tests/test_testutil.py

     
    1919            return "cookie not found" 
    2020    get_name = turbogears.expose()(get_name) 
    2121 
     22#testutil.start_server() 
    2223 
    2324def test_browser_session(): 
    2425    cherrypy.root = MyRoot() 
     
    3637    bs1.goto('/set_name?name=bs1') 
    3738    bs2.goto('/set_name?name=bs2') 
    3839    bs1.goto('/get_name') 
    39     assert bs1.response == 'bs1' 
     40    assert bs1.response == 'bs1', bs1.response 
    4041    bs2.goto('/get_name') 
    4142    assert bs2.response == 'bs2' 
  • turbogears/tests/test_catwalk.py

     
    4747 
    4848    def test_wrong_filter_format(self): 
    4949        cherrypy.root.catwalk = CatWalk(browse) 
    50         testutil.create_request("/catwalk/browse/?object_name=Song&filters=Guantanemera&tg_format=json") 
    51         response = cherrypy.response.body[0] 
    52         assert 'filter_format_error' in response 
     50        response = testutil.create_request("/catwalk/browse/?object_name=Song&filters=Guantanemera&tg_format=json") 
     51        assert 'filter_format_error' in response.body 
    5352 
    5453    def test_wrong_filter_column(self): 
    5554        cherrypy.root.catwalk = CatWalk(browse) 
    56         testutil.create_request("/catwalk/browse/?object_name=Song&filters=guacamole:2&tg_format=json") 
    57         response = cherrypy.response.body[0] 
    58         assert 'filter_column_error' in response 
     55        response = testutil.create_request("/catwalk/browse/?object_name=Song&filters=guacamole:2&tg_format=json") 
     56        assert 'filter_column_error' in response.body 
    5957 
    6058    def test_filters(self): 
    6159        cherrypy.root.catwalk = CatWalk(browse) 
    62         testutil.create_request("/catwalk/browse/?object_name=Song&tg_format=json") 
    63         response = cherrypy.response.body[0] 
    64         values = simplejson.loads(response) 
     60        reponse = testutil.create_request("/catwalk/browse/?object_name=Song&tg_format=json") 
     61        values = simplejson.loads(response.body) 
    6562        assert values['total'] == 15 * 15 * 15 #without the filters we get all songs (3375) 
    66         testutil.create_request("/catwalk/browse/?object_name=Song&filters=album:1&tg_format=json") 
    67         response = cherrypy.response.body[0] 
    68         values = simplejson.loads(response) 
     63        response = testutil.create_request("/catwalk/browse/?object_name=Song&filters=album:1&tg_format=json") 
     64        values = simplejson.loads(response.body) 
    6965        assert values['total'] == 15 #filter by album id (only 15 songs) 
    7066 
    7167    def test_response_fields(self): 
    7268        #Check that the response contains the expected keys 
    7369        cherrypy.root.catwalk = CatWalk(browse) 
    74         testutil.create_request("/catwalk/browse/?object_name=Artist&start=3&page_size=20&tg_format=json") 
    75         response = cherrypy.response.body[0] 
    76         values = simplejson.loads(response) 
     70        response = testutil.create_request("/catwalk/browse/?object_name=Artist&start=3&page_size=20&tg_format=json") 
     71        values = simplejson.loads(response.body) 
    7772        assert values.has_key('headers') 
    7873        assert values.has_key('rows') 
    7974        assert values.has_key('start') 
     
    8782        #Control that the count for related and multiple joins match 
    8883        #the number of related instances when accessed as a field 
    8984        cherrypy.root.catwalk = CatWalk(browse) 
    90         testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
    91         response = cherrypy.response.body[0] 
    92         values = simplejson.loads(response) 
     85        response = testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
     86        values = simplejson.loads(response.body) 
    9387        artist = browse.Artist.get(1) 
    9488        assert int(values['rows'][0]['genres']) == len(list(artist.genres)) 
    9589        assert int(values['rows'][0]['albums']) == len(list(artist.albums)) 
     
    9791    def test_rows_column_number(self): 
    9892        #Control that the number of columns match the number of fields in the model 
    9993        cherrypy.root.catwalk = CatWalk(browse) 
    100         testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
    101         response = cherrypy.response.body[0] 
    102         values = simplejson.loads(response) 
     94        response = testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
     95        values = simplejson.loads(response.body) 
    10396        assert len(values['rows'][0]) == 4 
    10497 
    10598    def test_rows_limit(self): 
    10699        #Update the limit of rows for the query and control the number of rows returned 
    107100        cherrypy.root.catwalk = CatWalk(browse) 
    108         testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
    109         response = cherrypy.response.body[0] 
    110         values = simplejson.loads(response) 
     101        response = testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
     102        values = simplejson.loads(response.body) 
    111103        assert values.has_key('rows') 
    112104        assert len(values['rows']) == 10 
    113         testutil.create_request("/catwalk/browse/?object_name=Artist&page_size=15&tg_format=json") 
    114         response = cherrypy.response.body[0] 
    115         values = simplejson.loads(response) 
     105 
     106        response = testutil.create_request("/catwalk/browse/?object_name=Artist&page_size=15&tg_format=json") 
     107        values = simplejson.loads(response.body) 
    116108        assert values.has_key('rows') 
    117109        assert len(values['rows']) == 15 
    118110 
    119111    def test_header_labels(self): 
    120112        #Check that the returned header labels match the the model 
    121113        cherrypy.root.catwalk = CatWalk(browse) 
    122         testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
    123         response = cherrypy.response.body[0] 
    124         values = simplejson.loads(response) 
     114        response = testutil.create_request("/catwalk/browse/?object_name=Artist&tg_format=json") 
     115        values = simplejson.loads(response.body) 
    125116        assert len(values['headers']) == 5 
    126117        for header in values['headers']: 
    127118            assert header['name'] in ['id','name','albums','genres', 'plays_instruments'] 
  • turbogears/tests/test_paginate.py

     
    138138        cherrypy.root = self.MyRoot() 
    139139 
    140140    def test_spy(self): 
    141         create_request('/spy') 
    142         body = cherrypy.response.body[0] 
    143         Spy.assert_ok(body, 'current_page', 1) 
     141        response = create_request('/spy') 
     142        Spy.assert_ok(response.body, 'current_page', 1) 
    144143        try: 
    145             Spy.assert_ok(body, 'current_page', 2) 
     144            Spy.assert_ok(response.body, 'current_page', 2) 
    146145            raise Exception("above test should have failed") 
    147146        except AssertionError: 
    148147            pass 
    149148 
    150149    def test_correct_expectation(self): 
    151         create_request('/spy_correct_expectation') 
    152         body = cherrypy.response.body[0] 
    153         assert "ok: [paginate" in body 
     150        response = create_request('/spy_correct_expectation') 
     151        assert "ok: [paginate" in response.body 
    154152 
    155153    def test_wrong_expectation(self): 
    156         create_request('/spy_wrong_expectation') 
    157         body = cherrypy.response.body[0] 
    158         assert "fail: expected page_count=9, got page_count=10" in body 
     154        response = create_request('/spy_wrong_expectation') 
     155        assert "fail: expected page_count=9, got page_count=10" in response.body 
    159156 
    160157    def test_invalid_expectation(self): 
    161         create_request('/spy_invalid_expectation') 
    162         body = cherrypy.response.body[0] 
    163         assert "fail: paginate does not have 'foobar' attribute" in body 
     158        response = create_request('/spy_invalid_expectation') 
     159        assert "fail: paginate does not have 'foobar' attribute" in response.body 
    164160 
    165161    def test_raw_expectation(self): 
    166         create_request('/spy_correct_expectation') 
    167         Spy.assert_ok(cherrypy.response.body[0], 'var_name', 'data') 
    168         Spy.assert_ok(cherrypy.response.body[0], 'var_name', "'data'", raw=True) 
     162        response = create_request('/spy_correct_expectation') 
     163        Spy.assert_ok(response.body, 'var_name', 'data') 
     164        Spy.assert_ok(response.body, 'var_name', "'data'", raw=True) 
    169165 
    170166 
    171167class TestPagination(unittest.TestCase): 
    172168    """Base class for all Paginate TestCases""" 
    173169 
    174     def request(self, url): 
    175         create_request(url) 
    176         self.body = cherrypy.response.body[0] 
     170    def request(self, url, status=None): 
     171        self.body = create_request(url, status=status).body 
    177172        if "fail: " in self.body: 
    178173            print self.body 
    179             assert False, "Spy alert! Check body output for details..." 
     174            assert False, "Spy alert! Check response output for details..." 
    180175 
    181176 
    182177class TestBasicPagination(TestPagination): 
     
    358353        Spy.assert_ok(self.body, 'pages', [4, 5, 6, 7]) 
    359354 
    360355    def test_invalid_dynamic_limit(self): 
    361         self.request("/invalid_dynamic") 
     356        self.request("/invalid_dynamic", status=500) 
    362357        assert 'StandardError: dynamic_limit: foobar not found in output dict' in self.body 
    363358 
    364359    def test_dynamic_limit(self): 
     
    733728 
    734729    def test_invalid_default_reversed(self): 
    735730        for method in "Q", "SR", "SO": 
    736             self.request("/wrong_reversed/?method=%s" % method) 
     731            self.request("/wrong_reversed/?method=%s" % method, status=500) 
    737732            assert 'StandardError: default_reversed (deprecated) is only  allowed' in self.body 
    738733 
    739734    def test_reverse_ordering(self): 
  • turbogears/testutil.py

     
    44import unittest 
    55import Cookie 
    66import cStringIO as StringIO 
     7from turbogears.util import deprecated 
    78 
    89import cherrypy 
    9 from cherrypy import _cphttptools 
     10cherrypy_major_ver = int(cherrypy.__version__.split('.')[0]) 
     11if cherrypy_major_ver < 3: 
     12    from cherrypy._cphttptools import Request, Response 
     13else: 
     14    from cherrypy import Request, Response 
    1015 
    1116try: 
    1217    import sqlobject 
     
    5055config.update({'global': 
    5156        {'autoreload.on': False, 'tg.new_style_logging': True}}) 
    5257 
     58harness = config.get('test_framework', 'cp2_webob') 
     59#harness = config.get('test_framework', 'webtest') 
     60if harness == 'webtest': 
     61    from webtest import TestApp 
    5362 
    54 def start_cp(): 
     63def mount_app(app): 
     64    cherrypy.tree.mount(app) 
     65 
     66def start_server(root=None): 
     67    if root: 
     68        mount_app(root) 
    5569    if not config.get("cherrypy_started", False): 
    56         cherrypy.server.start(serverClass=None, initOnly=True) 
    57         config.update({"cherrypy_started" : True}) 
     70        if cherrypy_major_ver < 3: 
     71            cherrypy.server.start(serverClass=None, initOnly=True) 
     72        else: 
     73            mount_app(cherrypy.root) 
     74            cherrypy.server.quickstart() 
     75            cherrypy.engine.start() #blocking=False) 
     76    config.update({"cherrypy_started" : True}) 
    5877 
     78start_cp = deprecated("start_cp has been superceded by start_server.")(start_server) 
    5979 
    6080test_user = None 
    6181 
     
    7292            or current_provider.anonymous_identity()) 
    7393 
    7494 
    75 def create_request(request, method="GET", protocol="HTTP/1.1", 
    76         headers={}, rfile=None, clientAddress="127.0.0.1", 
    77         remoteHost="localhost", scheme="http"): 
    78     start_cp() 
    79     if not rfile: 
    80         rfile = StringIO.StringIO("") 
    81     if type(headers) != dict: 
    82         headerList = headers 
     95def get_app(): 
     96    if cherrypy_major_ver < 3: 
     97        app = cherrypy._cpwsgi.wsgiApp 
    8398    else: 
    84         headerList = [(key, value) for key, value in headers.items()] 
    85     headerList.append(("Host", "localhost")) 
    86     if not hasattr(cherrypy.root, "started"): 
    87         startup.startTurboGears() 
    88         cherrypy.root.started = True 
    89     req = _cphttptools.Request(clientAddress, 80, remoteHost, scheme) 
    90     cherrypy.serving.request = req 
    91     attach_identity(req) 
    92     cherrypy.serving.response = _cphttptools.Response() 
    93     req.run(" ".join((method, request, protocol)), headerList, rfile) 
     99        app = cherrypy.tree.mount(cherrypy.root, '/') #app.HelloWorld(), '/') 
     100        # app = cherrypy.wsgi.CPWSGIServer 
     101    return app 
    94102 
    95103 
     104class PsuedoWebObResponse(object): 
     105    """ A wrapper around CherryPy 2's response to make it look more like a WebOb response.""" 
     106    def __init__(self, cp_response, status=None): 
     107        self._cp_response = cp_response 
     108        #raise Exception(dir(cp_response), str(cp_response.body)) 
     109        try: 
     110            self.body = cp_response.body[0] 
     111        except TypeError: 
     112            self.body = None 
     113 
     114        self.status = cp_response.status 
     115        status_ok = ((status == None and self.status[0] in ['2', '3']) or 
     116                             (status==int(self.status[:3]))  or (status == '*')) 
     117        if self.status[0] != '5': 
     118            msg = (status, self.status) 
     119        else: 
     120            msg = status_ok, (status, self.status, self.body) 
     121        assert status_ok, msg 
     122 
     123        self.cookies = getattr(cp_response, 'simple_cookie', Cookie.SimpleCookie()) 
     124        self.cookies_set = self.cookies 
     125        self.headers = cp_response.headers 
     126        self.location = cp_response.headers.get('Location', None) 
     127 
     128    def __str__(self): 
     129        return self.body 
     130    __repr__ = __str__ 
     131 
     132 
     133def create_request(request, headers={}, cookies={}, status=None): 
     134    # Previously accepted the following parms, but they appeared to be unsed: 
     135    #   protocol, remoteHost, clientAddress, rfile, scheme, method. 
     136    # Also, headers used to be able to be a list or a dict, but only dict was ever used. 
     137    rfile=StringIO.StringIO("") 
     138    protocol="HTTP/1.1" 
     139    clientAddress="127.0.0.1" 
     140    remoteHost="localhost" 
     141    scheme="http" 
     142    method="GET" 
     143 
     144    #start_server() 
     145    header_list = [(key, value) for key, value in headers.items()] 
     146    if not headers.get("Host", None): 
     147        header_list.append(("Host", "localhost")) 
     148 
     149    if 1==1 or harness.startswith('cp2'): 
     150        if not hasattr(cherrypy.root, "started"): 
     151            startup.startTurboGears() 
     152            cherrypy.root.started = True 
     153    #startup.startTurboGears() 
     154    start_server() 
     155    if harness.startswith('cp2'): 
     156        req = Request(clientAddress, 80, remoteHost, scheme) 
     157        cherrypy.serving.request = req 
     158        attach_identity(req) 
     159        cherrypy.serving.response = Response() 
     160        req.run(" ".join((method, request, protocol)), header_list, rfile) 
     161        response = cherrypy.serving.response 
     162        #response = cherrypy.response 
     163        if harness == 'cp2_webob': 
     164            # Lightly massage cherrypy's response to make it look slightly more like webob's 
     165            response = PsuedoWebObResponse(cherrypy.response, status) 
     166 
     167            # I don't know why this is necessary, but several tests fail  
     168            # unless I pull the top element out :-( 
     169        #if isinstance(response.body, list): 
     170        #        response.body = response.body[0] 
     171        #        raise 'hah! %s' % response.body 
     172        #else: 
     173        #    raise 'wahh! %s' % type(response.body) 
     174        #elif harness != 'cp2_webob': 
     175        #    raise 'Huhh?! %s' % harness 
     176    else: 
     177        start_server() 
     178        app = TestApp(get_app()) 
     179        app.cookies = cookies 
     180        response = app.get(request, headers, status=status) 
     181        response.app = app 
     182        #raise Exception(dir(response)) 
     183    return response 
     184 
     185 
     186createRequest = deprecated("Use create_request instead of createRequest")(create_request) 
     187 
     188 
    96189class BrowsingSession(object): 
    97190 
    98191    def __init__(self): 
     
    104197        if self.cookie: 
    105198            headers = kwargs.setdefault('headers', {}) 
    106199            headers['Cookie'] = self.cookie.output() 
    107         create_request(*args, **kwargs) 
    108         self.response = cherrypy.response.body[0] 
    109         self.status = cherrypy.response.status 
    110         if cherrypy.response.simple_cookie: 
    111             self.cookie.update(cherrypy.response.simple_cookie) 
     200        response = create_request(*args, **kwargs) 
     201        self.response = response.body 
     202        self.status = response.status 
     203        self.cookie = response.cookies_set 
    112204 
    113205 
    114206def _return_directly(output, *args): 
     
    137229 
    138230 
    139231def call(method, *args, **kw): 
    140     start_cp() 
     232    start_server() 
    141233    output, response = call_with_request(method, DummyRequest(), *args, **kw) 
    142234    return output 
    143235 
     
    150242    """ 
    151243    orig_proc_output = controllers._process_output 
    152244    controllers._process_output = _return_directly 
    153     cherrypy.serving.response = _cphttptools.Response() 
     245    cherrypy.serving.response = Response() 
    154246    cherrypy.serving.request = request 
    155247    if not hasattr(request, "identity"): 
    156248        attach_identity(request) 
     
    301393 
    302394__all__ = ["call", "create_request", "DBTest", 
    303395    "attach_identity", "set_identity_user", 
    304     "capture_log", "print_log", "get_log", "sqlalchemy_cleanup"] 
     396    "capture_log", "print_log", "get_log", "sqlalchemy_cleanup", 
     397    "mount_app"] 
     398 
  • turbogears/util.py

     
    444444    return re.sub("&(\w+);?", repl, htmltext) 
    445445 
    446446 
     447# This decorator inspired by python cookbook recipe #391367. 
     448import warnings 
     449 
     450def deprecated(message=None): 
     451    """This is a decorator which can be used to mark functions 
     452    as deprecated. It will result in a warning being emmitted 
     453    when the function is used. 
     454     
     455    Examples:      
     456 
     457>>> import sys; _stderr = sys.stderr; sys.stderr = sys.stdout #allow doctest to see warnings 
     458 
     459>>> @deprecated('some_old_function has been deprecated.  Use "new_func" instead') 
     460... def some_old_function(x,y): 
     461...     return x + y 
     462>>> print some_old_function(1, 2) 
     463deprecated.py... DeprecationWarning: some_old_function has been deprecated.  Use "new_func" instead 
     464... 
     4653 
     466>>> class SomeClass: 
     467...     @deprecated() 
     468...     def some_old_method(self, x,y): 
     469...         return x + y 
     470>>> sc = SomeClass() 
     471>>> print sc.some_old_method(2, 3) 
     472deprecated.py... DeprecationWarning:... 
     4735 
     474>>> def my_func(x, y): 
     475...     return x + y 
     476>>> myFunc = deprecated("Use the PEP8 version")(my_func) 
     477>>> print myFunc(5, 2) 
     478deprecated.py... DeprecationWarning: Use the PEP8 version 
     479... 
     4807 
     481>>> sys.stderr = _stderr 
     482    """ 
     483    def decorate(func): 
     484        if not decorate.message: 
     485            decorate.message = "Call to deprecated function %s." % func.__name__ 
     486        def newFunc(*args, **kwargs): 
     487            warnings.warn(decorate.message, category=DeprecationWarning, stacklevel =2) 
     488            return func(*args, **kwargs) 
     489        newFunc.__name__ = func.__name__ 
     490        newFunc.__doc__ = func.__doc__ 
     491        newFunc.__dict__.update(func.__dict__) 
     492        return newFunc 
     493    decorate.message = message 
     494    return decorate 
     495 
     496 
    447497__all__ = ["Enum", "setlike", 
    448498           "get_package_name", "get_model", "load_project_config", 
    449499           "url", "ensure_sequence", "has_arg", 
  • turbogears/view/base.py

     
    9090    engine = engines.get(enginename, None) 
    9191    if not engine: 
    9292        raise KeyError, \ 
    93             "Template engine %s is not installed" % enginename 
     93            "Template engine %s is not installed.  Installed are: %s" % (enginename, engines) 
    9494    return engine, template, enginename 
    9595 
    9696