~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/testing/yuixhr.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-10-13 20:25:36 UTC
  • mfrom: (14124.4.3 bug872089)
  • Revision ID: launchpad@pqm.canonical.com-20111013202536-3tzifmuw2bzrjo8j
[r=gmb][bug=872089][no-qa] bugfixes and improvements to the yuixhr
        test infrastructure

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
    'YUITestFixtureControllerView',
12
12
]
13
13
 
 
14
from fnmatch import fnmatchcase
14
15
import os
15
16
import simplejson
16
17
import sys
158
159
    HTML = 'HTML'
159
160
    SETUP = 'SETUP'
160
161
    TEARDOWN = 'TEARDOWN'
 
162
    INDEX = 'INDEX'
161
163
 
162
164
    page_template = dedent("""\
163
165
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
169
171
            src="/+icing/rev%(revno)s/build/launchpad.js"></script>
170
172
          <link rel="stylesheet"
171
173
            href="/+icing/yui/assets/skins/sam/skin.css"/>
 
174
          <link type="text/css" rel="stylesheet" media="screen, print"
 
175
                href="https://fonts.googleapis.com/css?family=Ubuntu:400,400italic,700,700italic" />
172
176
          <link rel="stylesheet" href="/+icing/rev%(revno)s/combo.css"/>
173
177
          <style>
174
178
          /* Taken and customized from testlogger.css */
193
197
        </head>
194
198
        <body class="yui3-skin-sam">
195
199
          <div id="log"></div>
 
200
          <p>Want to re-run your test?</p>
 
201
          <ul>
 
202
            <li><a href="?">Reload test JS</a></li>
 
203
            <li><a href="?reload=1">Reload test JS and the associated
 
204
                                    Python fixtures</a></li>
 
205
          </ul>
 
206
          <p>Don't forget to run <code>make jsbuild</code> and then do a
 
207
             hard reload of this page if you change a file that is built
 
208
             into launchpad.js!</p>
 
209
          <p>If you change Python code other than the fixtures, you must
 
210
             restart the server.  Sorry.</p>
 
211
        </body>
 
212
        </html>
 
213
        """)
 
214
 
 
215
    index_template = dedent("""\
 
216
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 
217
          "http://www.w3.org/TR/html4/strict.dtd">
 
218
        <html>
 
219
          <head>
 
220
          <title>YUI XHR Tests</title>
 
221
          <script type="text/javascript"
 
222
            src="/+icing/rev%(revno)s/build/launchpad.js"></script>
 
223
          <link type="text/css" rel="stylesheet" media="screen, print"
 
224
                href="https://fonts.googleapis.com/css?family=Ubuntu:400,400italic,700,700italic" />
 
225
          <link rel="stylesheet"
 
226
            href="/+icing/yui/assets/skins/sam/skin.css"/>
 
227
          <link rel="stylesheet" href="/+icing/rev%(revno)s/combo.css"/>
 
228
          <style>
 
229
          ul {
 
230
            text-align: left;
 
231
          }
 
232
          body, ul, h1 {
 
233
            margin: 0.3em;
 
234
            padding: 0.3em;
 
235
          }
 
236
        </style>
 
237
        </head>
 
238
        <body class="yui3-skin-sam">
 
239
          <h1>YUI XHR Tests</h1>
 
240
          <ul>%(tests)s</ul>
196
241
        </body>
197
242
        </html>
198
243
        """)
208
253
        return os.path.join(*self.names)
209
254
 
210
255
    def initialize(self):
 
256
        if not self.names:
 
257
            self.action = self.INDEX
 
258
            return
211
259
        path, ext = os.path.splitext(self.traversed_path)
212
260
        full_path = os.path.join(config.root, 'lib', path)
213
261
        if not os.path.exists(full_path + '.py'):
236
284
        assert os.path.sep not in name, (
237
285
            'traversed name contains os.path.sep: %s' % name)
238
286
        assert name != '..', 'traversing to ..'
239
 
        self.names.append(name)
 
287
        if name:
 
288
            self.names.append(name)
240
289
        return self
241
290
 
242
291
    def browserDefault(self, request):
243
292
        return self, ()
244
293
 
245
 
    def page(self):
 
294
    @property
 
295
    def module_name(self):
 
296
        return '.'.join(self.names)
 
297
 
 
298
    def get_fixtures(self):
 
299
        module = __import__(
 
300
            self.module_name, globals(), locals(), ['_fixtures_'], 0)
 
301
        return module._fixtures_
 
302
 
 
303
    def renderINDEX(self):
 
304
        root = os.path.join(config.root, 'lib')
 
305
        test_lines = []
 
306
        for path in find_tests(root):
 
307
            test_path = '/+yuitest/' + '/'.join(path)
 
308
            module_name = '.'.join(path)
 
309
            try:
 
310
                module = __import__(
 
311
                    module_name, globals(), locals(), ['test_suite'], 0)
 
312
            except ImportError:
 
313
                warning = 'cannot import Python fixture file'
 
314
            else:
 
315
                try:
 
316
                    suite_factory = module.test_suite
 
317
                except AttributeError:
 
318
                    warning = 'cannot find test_suite'
 
319
                else:
 
320
                    try:
 
321
                        suite = suite_factory()
 
322
                    except EXPLOSIVE_ERRORS:
 
323
                        raise
 
324
                    except:
 
325
                        warning = 'test_suite raises errors'
 
326
                    else:
 
327
                        case = None
 
328
                        for case in suite:
 
329
                            if isinstance(case, YUIAppServerTestCase):
 
330
                                root_url = config.appserver_root_url(
 
331
                                    case.facet)
 
332
                                if root_url != 'None':
 
333
                                    test_path = root_url + test_path
 
334
                                warning = ''
 
335
                                break
 
336
                        else:
 
337
                            warning = (
 
338
                                'test suite is not instance of '
 
339
                                'YUIAppServerTestCase')
 
340
            link = '<a href="%s">%s</a>' % (test_path, test_path)
 
341
            if warning:
 
342
                warning = ' <span class="warning">%s</span>' % warning
 
343
            test_lines.append('<li>%s%s</li>' % (link, warning))
 
344
        return self.index_template % {
 
345
            'revno': revno,
 
346
            'tests': '\n'.join(test_lines)}
 
347
 
 
348
    def renderJAVASCRIPT(self):
 
349
        self.request.response.setHeader('Content-Type', 'text/javascript')
 
350
        self.request.response.setHeader('Cache-Control', 'no-cache')
 
351
        return open(
 
352
            os.path.join(config.root, 'lib', self.traversed_path))
 
353
 
 
354
    def renderHTML(self):
 
355
        self.request.response.setHeader('Content-Type', 'text/html')
 
356
        self.request.response.setHeader('Cache-Control', 'no-cache')
 
357
        if ('INTERACTIVE_TESTS' in os.environ and
 
358
            'reload' in self.request.form):
 
359
            # We should try to reload the module.
 
360
            module = sys.modules.get(self.module_name)
 
361
            if module is not None:
 
362
                del module._fixtures_
 
363
                reload(module)
246
364
        return self.page_template % dict(
247
365
            test_module='/+yuitest/%s.js' % self.traversed_path,
248
366
            revno=revno)
249
367
 
250
 
    def get_fixtures(self):
251
 
        module_name = '.'.join(self.names)
252
 
        test_module = __import__(
253
 
            module_name, globals(), locals(), ['_fixtures_'], 0)
254
 
        return test_module._fixtures_
 
368
    def renderSETUP(self):
 
369
        data = {}
 
370
        fixtures = self.get_fixtures()
 
371
        try:
 
372
            for fixture_name in self.fixtures:
 
373
                __traceback_info__ = (fixture_name, data)
 
374
                fixtures[fixture_name](self.request, data)
 
375
        except EXPLOSIVE_ERRORS:
 
376
            raise
 
377
        except:
 
378
            self.request.response.setStatus(500)
 
379
            result = ''.join(format_exception(*sys.exc_info()))
 
380
        else:
 
381
            self.request.response.setHeader(
 
382
                'Content-Type', 'application/json')
 
383
            # We use the ProxyFactory so that the restful
 
384
            # redaction code is always used.
 
385
            result = simplejson.dumps(
 
386
                ProxyFactory(data), cls=ResourceJSONEncoder)
 
387
        return result
 
388
 
 
389
    def renderTEARDOWN(self):
 
390
        data = simplejson.loads(self.request.form['data'])
 
391
        fixtures = self.get_fixtures()
 
392
        try:
 
393
            for fixture_name in reversed(self.fixtures):
 
394
                __traceback_info__ = (fixture_name, data)
 
395
                fixtures[fixture_name].teardown(self.request, data)
 
396
        except EXPLOSIVE_ERRORS:
 
397
            raise
 
398
        except:
 
399
            self.request.response.setStatus(500)
 
400
            result = ''.join(format_exception(*sys.exc_info()))
 
401
        else:
 
402
            # Remove the session cookie, in case we have one.
 
403
            self.request.response.expireCookie(
 
404
                getUtility(IClientIdManager).namespace)
 
405
            # Blow up the database once we are out of this transaction
 
406
            # by passing a result that will do so when it is iterated
 
407
            # through in asyncore.
 
408
            self.request.response.setHeader('Content-Length', 1)
 
409
            result = CloseDbResult()
 
410
        return result
255
411
 
256
412
    def render(self):
257
 
        if self.action == self.JAVASCRIPT:
258
 
            self.request.response.setHeader('Content-Type', 'text/javascript')
259
 
            result = open(
260
 
                os.path.join(config.root, 'lib', self.traversed_path))
261
 
        elif self.action == self.HTML:
262
 
            self.request.response.setHeader('Content-Type', 'text/html')
263
 
            result = self.page()
264
 
        elif self.action == self.SETUP:
265
 
            data = {}
266
 
            fixtures = self.get_fixtures()
267
 
            try:
268
 
                for fixture_name in self.fixtures:
269
 
                    __traceback_info__ = (fixture_name, data)
270
 
                    fixtures[fixture_name](self.request, data)
271
 
            except EXPLOSIVE_ERRORS:
272
 
                raise
273
 
            except:
274
 
                self.request.response.setStatus(500)
275
 
                result = ''.join(format_exception(*sys.exc_info()))
276
 
            else:
277
 
                self.request.response.setHeader(
278
 
                    'Content-Type', 'application/json')
279
 
                # We use the ProxyFactory so that the restful
280
 
                # redaction code is always used.
281
 
                result = simplejson.dumps(
282
 
                    ProxyFactory(data), cls=ResourceJSONEncoder)
283
 
        elif self.action == self.TEARDOWN:
284
 
            data = simplejson.loads(self.request.form['data'])
285
 
            fixtures = self.get_fixtures()
286
 
            try:
287
 
                for fixture_name in reversed(self.fixtures):
288
 
                    __traceback_info__ = (fixture_name, data)
289
 
                    fixtures[fixture_name].teardown(self.request, data)
290
 
            except EXPLOSIVE_ERRORS:
291
 
                raise
292
 
            except:
293
 
                self.request.response.setStatus(500)
294
 
                result = ''.join(format_exception(*sys.exc_info()))
295
 
            else:
296
 
                # Remove the session cookie, in case we have one.
297
 
                self.request.response.expireCookie(
298
 
                    getUtility(IClientIdManager).namespace)
299
 
                # Blow up the database once we are out of this transaction
300
 
                # by passing a result that will do so when it is iterated
301
 
                # through in asyncore.
302
 
                self.request.response.setHeader('Content-Length', 1)
303
 
                result = CloseDbResult()
304
 
        return result
 
413
        return getattr(self, 'render' + self.action)()
 
414
 
 
415
 
 
416
def find_tests(root):
 
417
    for dirpath, dirnames, filenames in os.walk(root):
 
418
        dirpath = os.path.relpath(dirpath, root)
 
419
        for filename in filenames:
 
420
            if fnmatchcase(filename, 'test_*.js'):
 
421
                name, ext = os.path.splitext(filename)
 
422
                if name + '.py' in filenames:
 
423
                    names = dirpath.split(os.path.sep)
 
424
                    names.append(name)
 
425
                    yield names
305
426
 
306
427
 
307
428
# This class cannot be imported directly into a test suite because
313
434
    layer = YUIAppServerLayer
314
435
    _testMethodName = 'runTest'
315
436
 
316
 
    def __init__(self, module_name=None):
 
437
    def __init__(self, module_name, facet='mainsite'):
317
438
        self.module_name = module_name
 
439
        self.facet = facet
318
440
        # This needs to be done early so the "id" is set correctly.
319
441
        self.test_path = self.module_name.replace('.', '/')
320
442
        super(YUIAppServerTestCase, self).__init__()
321
443
 
322
444
    def setUp(self):
323
 
        root_url = LayerProcessController.appserver_root_url()
324
 
        self.html_uri = '%s+yuitest/%s' % (root_url, self.test_path)
 
445
        config = LayerProcessController.appserver_config
 
446
        root_url = config.appserver_root_url(self.facet)
 
447
        self.html_uri = '%s/+yuitest/%s' % (root_url, self.test_path)
325
448
        super(YUIAppServerTestCase, self).setUp()
326
449
 
327
450
    runTest = AbstractYUITestCase.checkResults
328
451
 
329
452
 
330
 
def make_suite(module_name):
331
 
    return unittest.TestSuite([YUIAppServerTestCase(module_name)])
 
453
def make_suite(module_name, facet='mainsite'):
 
454
    return unittest.TestSuite([YUIAppServerTestCase(module_name, facet)])