diff -r 215ecdb5f047 -r 1368c80276bc devtools/testlib.py --- a/devtools/testlib.py Fri Jan 30 15:30:55 2009 +0100 +++ b/devtools/testlib.py Fri Jan 30 15:32:02 2009 +0100 @@ -93,28 +93,24 @@ # SaxOnlyValidator : guarantees XML is well formed # None : do not try to validate anything # validators used must be imported from from.devtools.htmlparser - validators = { - # maps vid : validator name - 'hcal' : SaxOnlyValidator, - 'rss' : SaxOnlyValidator, - 'rssitem' : None, - 'xml' : SaxOnlyValidator, - 'xmlitem' : None, - 'xbel' : SaxOnlyValidator, - 'xbelitem' : None, - 'vcard' : None, - 'fulltext': None, - 'fullthreadtext': None, - 'fullthreadtext_descending': None, - 'text' : None, - 'treeitemview': None, - 'textincontext' : None, - 'textoutofcontext' : None, - 'combobox' : None, - 'csvexport' : None, - 'ecsvexport' : None, - 'owl' : SaxOnlyValidator, # XXX - 'owlabox' : SaxOnlyValidator, # XXX + content_type_validators = { + # maps MIME type : validator name + # + # do not set html validators here, we need HTMLValidator for html + # snippets + #'text/html': DTDValidator, + #'application/xhtml+xml': DTDValidator, + 'application/xml': SaxOnlyValidator, + 'text/xml': SaxOnlyValidator, + 'text/plain': None, + 'text/comma-separated-values': None, + 'text/x-vcard': None, + 'text/calendar': None, + 'application/json': None, + 'image/png': None, + } + vid_validators = { + # maps vid : validator name (override content_type_validators) } valmap = {None: None, 'dtd': DTDValidator, 'xml': SaxOnlyValidator} no_auto_populate = () @@ -165,21 +161,24 @@ self.commit() @nocoverage - def _check_html(self, output, vid, template='main'): + def _check_html(self, output, view, template='main'): """raises an exception if the HTML is invalid""" - if template is None: - default_validator = HTMLValidator - else: - default_validator = DTDValidator - validatorclass = self.validators.get(vid, default_validator) + try: + validatorclass = self.vid_validators[view.id] + except KeyError: + if template is None: + default_validator = HTMLValidator + else: + default_validator = DTDValidator + validatorclass = self.content_type_validators.get(view.content_type, + default_validator) if validatorclass is None: return None validator = validatorclass() - output = output.strip() - return validator.parse_string(output) + return validator.parse_string(output.strip()) - def view(self, vid, rset, req=None, template='main', htmlcheck=True, **kwargs): + def view(self, vid, rset, req=None, template='main', **kwargs): """This method tests the view `vid` on `rset` using `template` If no error occured while rendering the view, the HTML is analyzed @@ -196,8 +195,6 @@ # print req.form['vid'] = vid view = self.vreg.select_view(vid, req, rset, **kwargs) - if view.content_type not in ('application/xml', 'application/xhtml+xml', 'text/html'): - htmlcheck = False # set explicit test description if rset is not None: self.set_description("testing %s, mod=%s (%s)" % (vid, view.__module__, rset.printable_rql())) @@ -211,13 +208,13 @@ # patch TheMainTemplate.process_rql to avoid recomputing resultset TheMainTemplate._select_view_and_rset = lambda *a, **k: (view, rset) try: - return self._test_view(viewfunc, vid, htmlcheck, template, **kwargs) + return self._test_view(viewfunc, view, template, **kwargs) finally: if template == 'main': TheMainTemplate._select_view_and_rset = _select_view_and_rset - def _test_view(self, viewfunc, vid, htmlcheck=True, template='main', **kwargs): + def _test_view(self, viewfunc, view, template='main', **kwargs): """this method does the actual call to the view If no error occured while rendering the view, the HTML is analyzed @@ -229,10 +226,7 @@ output = None try: output = viewfunc(**kwargs) - if htmlcheck: - return self._check_html(output, vid, template) - else: - return output + return self._check_html(output, view, template) except (SystemExit, KeyboardInterrupt): raise except: @@ -240,19 +234,16 @@ # is not an AssertionError klass, exc, tcbk = sys.exc_info() try: - msg = '[%s in %s] %s' % (klass, vid, exc) + msg = '[%s in %s] %s' % (klass, view.id, exc) except: - msg = '[%s in %s] undisplayable exception' % (klass, vid) + msg = '[%s in %s] undisplayable exception' % (klass, view.id) if output is not None: position = getattr(exc, "position", (0,))[0] if position: # define filter - - output = output.splitlines() width = int(log(len(output), 10)) + 1 line_template = " %" + ("%i" % width) + "i: %s" - # XXX no need to iterate the whole file except to get # the line number output = '\n'.join(line_template % (idx + 1, line) @@ -286,23 +277,26 @@ """returns the list of views that can be applied on `rset`""" req = rset.req only_once_vids = ('primary', 'secondary', 'text') - skipped = ('restriction', 'cell') req.data['ex'] = ValueError("whatever") for vid, views in self.vreg.registry('views').items(): if vid[0] == '_': continue - try: - view = self.vreg.select(views, req, rset) - if view.id in skipped: - continue - if view.category == 'startupview': + if rset.rowcount > 1 and vid in only_once_vids: + continue + views = [view for view in views + if view.category != 'startupview' + and not issubclass(view, NotificationView)] + if views: + try: + view = self.vreg.select(views, req, rset) + if view.linkable(): + yield view + else: + not_selected(self.vreg, view) + # else the view is expected to be used as subview and should + # not be tested directly + except NoSelectableObject: continue - if rset.rowcount > 1 and view.id in only_once_vids: - continue - if not isinstance(view, NotificationView): - yield view - except NoSelectableObject: - continue def list_actions_for(self, rset): """returns the list of actions that can be applied on `rset`""" @@ -310,22 +304,21 @@ for action in self.vreg.possible_objects('actions', req, rset): yield action - def list_boxes_for(self, rset): """returns the list of boxes that can be applied on `rset`""" req = rset.req for box in self.vreg.possible_objects('boxes', req, rset): yield box - def list_startup_views(self): """returns the list of startup views""" req = self.request() for view in self.vreg.possible_views(req, None): - if view.category != 'startupview': - continue - yield view.id - + if view.category == 'startupview': + yield view.id + else: + not_selected(self.vreg, view) + def _test_everything_for(self, rset): """this method tries to find everything that can be tested for `rset` and yields a callable test (as needed in generative tests) @@ -339,7 +332,7 @@ backup_rset = rset._prepare_copy(rset.rows, rset.description) yield InnerTest(self._testname(rset, view.id, 'view'), self.view, view.id, rset, - rset.req.reset_headers(), 'main', not view.binary) + rset.req.reset_headers(), 'main') # We have to do this because some views modify the # resultset's syntax tree rset = backup_rset @@ -352,8 +345,6 @@ for box in self.list_boxes_for(rset): yield InnerTest(self._testname(rset, box.id, 'box'), box.dispatch) - - @staticmethod def _testname(rset, objid, objtype): return '%s_%s_%s' % ('_'.join(rset.column_types(0)), objid, objtype) @@ -394,4 +385,36 @@ rset2 = rset.limit(limit=1, offset=row) yield rset2 +def not_selected(vreg, vobject): + try: + vreg._selected[vobject.__class__] -= 1 + except (KeyError, AttributeError): + pass +def vreg_instrumentize(testclass): + from cubicweb.devtools.apptest import TestEnvironment + env = testclass._env = TestEnvironment('data', configcls=testclass.configcls, + requestcls=testclass.requestcls) + vreg = env.vreg + vreg._selected = {} + orig_select = vreg.__class__.select + def instr_select(self, *args, **kwargs): + selected = orig_select(self, *args, **kwargs) + try: + self._selected[selected.__class__] += 1 + except KeyError: + self._selected[selected.__class__] = 1 + except AttributeError: + pass # occurs on vreg used to restore database + return selected + vreg.__class__.select = instr_select + +def print_untested_objects(testclass, skipregs=('hooks', 'etypes')): + vreg = testclass._env.vreg + for registry, vobjectsdict in vreg.items(): + if registry in skipregs: + continue + for vobjects in vobjectsdict.values(): + for vobject in vobjects: + if not vreg._selected.get(vobject): + print 'not tested', registry, vobject