web/lib/tagging/tests/tests.py
changeset 11 f236caaceb43
equal deleted inserted replaced
10:7207a5a27b8f 11:f236caaceb43
       
     1 # -*- coding: utf-8 -*-
       
     2 
       
     3 import os
       
     4 from django import forms
       
     5 from django.db.models import Q
       
     6 from django.test import TestCase
       
     7 from tagging.forms import TagField
       
     8 from tagging import settings
       
     9 from tagging.models import Tag, TaggedItem
       
    10 from tagging.tests.models import Article, Link, Perch, Parrot, FormTest
       
    11 from tagging.utils import calculate_cloud, edit_string_for_tags, get_tag_list, get_tag, parse_tag_input
       
    12 from tagging.utils import LINEAR
       
    13 
       
    14 #############
       
    15 # Utilities #
       
    16 #############
       
    17 
       
    18 class TestParseTagInput(TestCase):
       
    19     def test_with_simple_space_delimited_tags(self):
       
    20         """ Test with simple space-delimited tags. """
       
    21         
       
    22         self.assertEquals(parse_tag_input('one'), [u'one'])
       
    23         self.assertEquals(parse_tag_input('one two'), [u'one', u'two'])
       
    24         self.assertEquals(parse_tag_input('one two three'), [u'one', u'three', u'two'])
       
    25         self.assertEquals(parse_tag_input('one one two two'), [u'one', u'two'])
       
    26     
       
    27     def test_with_comma_delimited_multiple_words(self):
       
    28         """ Test with comma-delimited multiple words.
       
    29             An unquoted comma in the input will trigger this. """
       
    30             
       
    31         self.assertEquals(parse_tag_input(',one'), [u'one'])
       
    32         self.assertEquals(parse_tag_input(',one two'), [u'one two'])
       
    33         self.assertEquals(parse_tag_input(',one two three'), [u'one two three'])
       
    34         self.assertEquals(parse_tag_input('a-one, a-two and a-three'),
       
    35             [u'a-one', u'a-two and a-three'])
       
    36     
       
    37     def test_with_double_quoted_multiple_words(self):
       
    38         """ Test with double-quoted multiple words.
       
    39             A completed quote will trigger this.  Unclosed quotes are ignored. """
       
    40             
       
    41         self.assertEquals(parse_tag_input('"one'), [u'one'])
       
    42         self.assertEquals(parse_tag_input('"one two'), [u'one', u'two'])
       
    43         self.assertEquals(parse_tag_input('"one two three'), [u'one', u'three', u'two'])
       
    44         self.assertEquals(parse_tag_input('"one two"'), [u'one two'])
       
    45         self.assertEquals(parse_tag_input('a-one "a-two and a-three"'),
       
    46             [u'a-one', u'a-two and a-three'])
       
    47     
       
    48     def test_with_no_loose_commas(self):
       
    49         """ Test with no loose commas -- split on spaces. """
       
    50         self.assertEquals(parse_tag_input('one two "thr,ee"'), [u'one', u'thr,ee', u'two'])
       
    51         
       
    52     def test_with_loose_commas(self):
       
    53         """ Loose commas - split on commas """
       
    54         self.assertEquals(parse_tag_input('"one", two three'), [u'one', u'two three'])
       
    55         
       
    56     def test_tags_with_double_quotes_can_contain_commas(self):
       
    57         """ Double quotes can contain commas """
       
    58         self.assertEquals(parse_tag_input('a-one "a-two, and a-three"'),
       
    59             [u'a-one', u'a-two, and a-three'])
       
    60         self.assertEquals(parse_tag_input('"two", one, one, two, "one"'),
       
    61             [u'one', u'two'])
       
    62     
       
    63     def test_with_naughty_input(self):
       
    64         """ Test with naughty input. """
       
    65         
       
    66         # Bad users! Naughty users!
       
    67         self.assertEquals(parse_tag_input(None), [])
       
    68         self.assertEquals(parse_tag_input(''), [])
       
    69         self.assertEquals(parse_tag_input('"'), [])
       
    70         self.assertEquals(parse_tag_input('""'), [])
       
    71         self.assertEquals(parse_tag_input('"' * 7), [])
       
    72         self.assertEquals(parse_tag_input(',,,,,,'), [])
       
    73         self.assertEquals(parse_tag_input('",",",",",",","'), [u','])
       
    74         self.assertEquals(parse_tag_input('a-one "a-two" and "a-three'),
       
    75             [u'a-one', u'a-three', u'a-two', u'and'])
       
    76         
       
    77 class TestNormalisedTagListInput(TestCase):
       
    78     def setUp(self):
       
    79         self.cheese = Tag.objects.create(name='cheese')
       
    80         self.toast = Tag.objects.create(name='toast')
       
    81         
       
    82     def test_single_tag_object_as_input(self):
       
    83         self.assertEquals(get_tag_list(self.cheese), [self.cheese])
       
    84     
       
    85     def test_space_delimeted_string_as_input(self):
       
    86         ret = get_tag_list('cheese toast')
       
    87         self.assertEquals(len(ret), 2)
       
    88         self.failUnless(self.cheese in ret)
       
    89         self.failUnless(self.toast in ret)
       
    90         
       
    91     def test_comma_delimeted_string_as_input(self):
       
    92         ret = get_tag_list('cheese,toast')
       
    93         self.assertEquals(len(ret), 2)
       
    94         self.failUnless(self.cheese in ret)
       
    95         self.failUnless(self.toast in ret)
       
    96     
       
    97     def test_with_empty_list(self):
       
    98         self.assertEquals(get_tag_list([]), [])
       
    99     
       
   100     def test_list_of_two_strings(self):
       
   101         ret = get_tag_list(['cheese', 'toast'])
       
   102         self.assertEquals(len(ret), 2)
       
   103         self.failUnless(self.cheese in ret)
       
   104         self.failUnless(self.toast in ret)
       
   105     
       
   106     def test_list_of_tag_primary_keys(self):
       
   107         ret = get_tag_list([self.cheese.id, self.toast.id])
       
   108         self.assertEquals(len(ret), 2)
       
   109         self.failUnless(self.cheese in ret)
       
   110         self.failUnless(self.toast in ret)
       
   111     
       
   112     def test_list_of_strings_with_strange_nontag_string(self):
       
   113         ret = get_tag_list(['cheese', 'toast', 'ŠĐĆŽćžšđ'])
       
   114         self.assertEquals(len(ret), 2)
       
   115         self.failUnless(self.cheese in ret)
       
   116         self.failUnless(self.toast in ret)
       
   117     
       
   118     def test_list_of_tag_instances(self):
       
   119         ret = get_tag_list([self.cheese, self.toast])
       
   120         self.assertEquals(len(ret), 2)
       
   121         self.failUnless(self.cheese in ret)
       
   122         self.failUnless(self.toast in ret)
       
   123     
       
   124     def test_tuple_of_instances(self):
       
   125         ret = get_tag_list((self.cheese, self.toast))
       
   126         self.assertEquals(len(ret), 2)
       
   127         self.failUnless(self.cheese in ret)
       
   128         self.failUnless(self.toast in ret)
       
   129     
       
   130     def test_with_tag_filter(self):
       
   131         ret = get_tag_list(Tag.objects.filter(name__in=['cheese', 'toast']))
       
   132         self.assertEquals(len(ret), 2)
       
   133         self.failUnless(self.cheese in ret)
       
   134         self.failUnless(self.toast in ret)
       
   135         
       
   136     def test_with_invalid_input_mix_of_string_and_instance(self):
       
   137         try:
       
   138             get_tag_list(['cheese', self.toast])
       
   139         except ValueError, ve:
       
   140             self.assertEquals(str(ve),
       
   141                 'If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids.')
       
   142         except Exception, e:
       
   143             raise self.failureException('the wrong type of exception was raised: type [%s] value [%]' %\
       
   144                 (str(type(e)), str(e)))
       
   145         else:
       
   146             raise self.failureException('a ValueError exception was supposed to be raised!')
       
   147     
       
   148     def test_with_invalid_input(self):
       
   149         try:
       
   150             get_tag_list(29)
       
   151         except ValueError, ve:
       
   152             self.assertEquals(str(ve), 'The tag input given was invalid.')
       
   153         except Exception, e:
       
   154             raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' %\
       
   155                 (str(type(e)), str(e)))
       
   156         else:
       
   157             raise self.failureException('a ValueError exception was supposed to be raised!')
       
   158 
       
   159     def test_with_tag_instance(self):
       
   160         self.assertEquals(get_tag(self.cheese), self.cheese)
       
   161     
       
   162     def test_with_string(self):
       
   163         self.assertEquals(get_tag('cheese'), self.cheese)
       
   164     
       
   165     def test_with_primary_key(self):
       
   166         self.assertEquals(get_tag(self.cheese.id), self.cheese)
       
   167     
       
   168     def test_nonexistent_tag(self):
       
   169         self.assertEquals(get_tag('mouse'), None)
       
   170 
       
   171 class TestCalculateCloud(TestCase):
       
   172     def setUp(self):
       
   173         self.tags = []
       
   174         for line in open(os.path.join(os.path.dirname(__file__), 'tags.txt')).readlines():
       
   175             name, count = line.rstrip().split()
       
   176             tag = Tag(name=name)
       
   177             tag.count = int(count)
       
   178             self.tags.append(tag)
       
   179     
       
   180     def test_default_distribution(self):
       
   181         sizes = {}
       
   182         for tag in calculate_cloud(self.tags, steps=5):
       
   183             sizes[tag.font_size] = sizes.get(tag.font_size, 0) + 1
       
   184         
       
   185         # This isn't a pre-calculated test, just making sure it's consistent
       
   186         self.assertEquals(sizes[1], 48)
       
   187         self.assertEquals(sizes[2], 30)
       
   188         self.assertEquals(sizes[3], 19)
       
   189         self.assertEquals(sizes[4], 15)
       
   190         self.assertEquals(sizes[5], 10)
       
   191     
       
   192     def test_linear_distribution(self):
       
   193         sizes = {}
       
   194         for tag in calculate_cloud(self.tags, steps=5, distribution=LINEAR):
       
   195             sizes[tag.font_size] = sizes.get(tag.font_size, 0) + 1
       
   196         
       
   197         # This isn't a pre-calculated test, just making sure it's consistent
       
   198         self.assertEquals(sizes[1], 97)
       
   199         self.assertEquals(sizes[2], 12)
       
   200         self.assertEquals(sizes[3], 7)
       
   201         self.assertEquals(sizes[4], 2)
       
   202         self.assertEquals(sizes[5], 4)
       
   203     
       
   204     def test_invalid_distribution(self):
       
   205         try:
       
   206             calculate_cloud(self.tags, steps=5, distribution='cheese')
       
   207         except ValueError, ve:
       
   208             self.assertEquals(str(ve), 'Invalid distribution algorithm specified: cheese.')
       
   209         except Exception, e:
       
   210             raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' %\
       
   211                 (str(type(e)), str(e)))
       
   212         else:
       
   213             raise self.failureException('a ValueError exception was supposed to be raised!')
       
   214         
       
   215 ###########
       
   216 # Tagging #
       
   217 ###########
       
   218 
       
   219 class TestBasicTagging(TestCase):
       
   220     def setUp(self):
       
   221         self.dead_parrot = Parrot.objects.create(state='dead')
       
   222     
       
   223     def test_update_tags(self):
       
   224         Tag.objects.update_tags(self.dead_parrot, 'foo,bar,"ter"')
       
   225         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   226         self.assertEquals(len(tags), 3)
       
   227         self.failUnless(get_tag('foo') in tags)
       
   228         self.failUnless(get_tag('bar') in tags)
       
   229         self.failUnless(get_tag('ter') in tags)
       
   230         
       
   231         Tag.objects.update_tags(self.dead_parrot, '"foo" bar "baz"')
       
   232         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   233         self.assertEquals(len(tags), 3)
       
   234         self.failUnless(get_tag('bar') in tags)
       
   235         self.failUnless(get_tag('baz') in tags)
       
   236         self.failUnless(get_tag('foo') in tags)
       
   237     
       
   238     def test_add_tag(self):
       
   239         # start off in a known, mildly interesting state
       
   240         Tag.objects.update_tags(self.dead_parrot, 'foo bar baz')
       
   241         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   242         self.assertEquals(len(tags), 3)
       
   243         self.failUnless(get_tag('bar') in tags)
       
   244         self.failUnless(get_tag('baz') in tags)
       
   245         self.failUnless(get_tag('foo') in tags)
       
   246         
       
   247         # try to add a tag that already exists
       
   248         Tag.objects.add_tag(self.dead_parrot, 'foo')
       
   249         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   250         self.assertEquals(len(tags), 3)
       
   251         self.failUnless(get_tag('bar') in tags)
       
   252         self.failUnless(get_tag('baz') in tags)
       
   253         self.failUnless(get_tag('foo') in tags)
       
   254         
       
   255         # now add a tag that doesn't already exist
       
   256         Tag.objects.add_tag(self.dead_parrot, 'zip')
       
   257         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   258         self.assertEquals(len(tags), 4)
       
   259         self.failUnless(get_tag('zip') in tags)
       
   260         self.failUnless(get_tag('bar') in tags)
       
   261         self.failUnless(get_tag('baz') in tags)
       
   262         self.failUnless(get_tag('foo') in tags)
       
   263     
       
   264     def test_add_tag_invalid_input_no_tags_specified(self):
       
   265         # start off in a known, mildly interesting state
       
   266         Tag.objects.update_tags(self.dead_parrot, 'foo bar baz')
       
   267         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   268         self.assertEquals(len(tags), 3)
       
   269         self.failUnless(get_tag('bar') in tags)
       
   270         self.failUnless(get_tag('baz') in tags)
       
   271         self.failUnless(get_tag('foo') in tags)
       
   272         
       
   273         try:
       
   274             Tag.objects.add_tag(self.dead_parrot, '    ')
       
   275         except AttributeError, ae:
       
   276             self.assertEquals(str(ae), 'No tags were given: "    ".')
       
   277         except Exception, e:
       
   278             raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' %\
       
   279                 (str(type(e)), str(e)))
       
   280         else:
       
   281             raise self.failureException('an AttributeError exception was supposed to be raised!')
       
   282         
       
   283     def test_add_tag_invalid_input_multiple_tags_specified(self):
       
   284         # start off in a known, mildly interesting state
       
   285         Tag.objects.update_tags(self.dead_parrot, 'foo bar baz')
       
   286         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   287         self.assertEquals(len(tags), 3)
       
   288         self.failUnless(get_tag('bar') in tags)
       
   289         self.failUnless(get_tag('baz') in tags)
       
   290         self.failUnless(get_tag('foo') in tags)
       
   291         
       
   292         try:
       
   293             Tag.objects.add_tag(self.dead_parrot, 'one two')
       
   294         except AttributeError, ae:
       
   295             self.assertEquals(str(ae), 'Multiple tags were given: "one two".')
       
   296         except Exception, e:
       
   297             raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' %\
       
   298                 (str(type(e)), str(e)))
       
   299         else:
       
   300             raise self.failureException('an AttributeError exception was supposed to be raised!')
       
   301     
       
   302     def test_update_tags_exotic_characters(self):
       
   303         # start off in a known, mildly interesting state
       
   304         Tag.objects.update_tags(self.dead_parrot, 'foo bar baz')
       
   305         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   306         self.assertEquals(len(tags), 3)
       
   307         self.failUnless(get_tag('bar') in tags)
       
   308         self.failUnless(get_tag('baz') in tags)
       
   309         self.failUnless(get_tag('foo') in tags)
       
   310         
       
   311         Tag.objects.update_tags(self.dead_parrot, u'ŠĐĆŽćžšđ')
       
   312         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   313         self.assertEquals(len(tags), 1)
       
   314         self.assertEquals(tags[0].name, u'ŠĐĆŽćžšđ')
       
   315         
       
   316         Tag.objects.update_tags(self.dead_parrot, u'你好')
       
   317         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   318         self.assertEquals(len(tags), 1)
       
   319         self.assertEquals(tags[0].name, u'你好')
       
   320     
       
   321     def test_update_tags_with_none(self):
       
   322         # start off in a known, mildly interesting state
       
   323         Tag.objects.update_tags(self.dead_parrot, 'foo bar baz')
       
   324         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   325         self.assertEquals(len(tags), 3)
       
   326         self.failUnless(get_tag('bar') in tags)
       
   327         self.failUnless(get_tag('baz') in tags)
       
   328         self.failUnless(get_tag('foo') in tags)
       
   329         
       
   330         Tag.objects.update_tags(self.dead_parrot, None)
       
   331         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   332         self.assertEquals(len(tags), 0)
       
   333 
       
   334 class TestModelTagField(TestCase):
       
   335     """ Test the 'tags' field on models. """
       
   336     
       
   337     def test_create_with_tags_specified(self):
       
   338         f1 = FormTest.objects.create(tags=u'test3 test2 test1')
       
   339         tags = Tag.objects.get_for_object(f1)
       
   340         test1_tag = get_tag('test1')
       
   341         test2_tag = get_tag('test2')
       
   342         test3_tag = get_tag('test3')
       
   343         self.failUnless(None not in (test1_tag, test2_tag, test3_tag))
       
   344         self.assertEquals(len(tags), 3)
       
   345         self.failUnless(test1_tag in tags)
       
   346         self.failUnless(test2_tag in tags)
       
   347         self.failUnless(test3_tag in tags)
       
   348     
       
   349     def test_update_via_tags_field(self):
       
   350         f1 = FormTest.objects.create(tags=u'test3 test2 test1')
       
   351         tags = Tag.objects.get_for_object(f1)
       
   352         test1_tag = get_tag('test1')
       
   353         test2_tag = get_tag('test2')
       
   354         test3_tag = get_tag('test3')
       
   355         self.failUnless(None not in (test1_tag, test2_tag, test3_tag))
       
   356         self.assertEquals(len(tags), 3)
       
   357         self.failUnless(test1_tag in tags)
       
   358         self.failUnless(test2_tag in tags)
       
   359         self.failUnless(test3_tag in tags)
       
   360         
       
   361         f1.tags = u'test4'
       
   362         f1.save()
       
   363         tags = Tag.objects.get_for_object(f1)
       
   364         test4_tag = get_tag('test4')
       
   365         self.assertEquals(len(tags), 1)
       
   366         self.assertEquals(tags[0], test4_tag)
       
   367         
       
   368         f1.tags = ''
       
   369         f1.save()
       
   370         tags = Tag.objects.get_for_object(f1)
       
   371         self.assertEquals(len(tags), 0)
       
   372         
       
   373 class TestSettings(TestCase):
       
   374     def setUp(self):
       
   375         self.original_force_lower_case_tags = settings.FORCE_LOWERCASE_TAGS
       
   376         self.dead_parrot = Parrot.objects.create(state='dead')
       
   377     
       
   378     def tearDown(self):
       
   379         settings.FORCE_LOWERCASE_TAGS = self.original_force_lower_case_tags
       
   380     
       
   381     def test_force_lowercase_tags(self):
       
   382         """ Test forcing tags to lowercase. """
       
   383         
       
   384         settings.FORCE_LOWERCASE_TAGS = True
       
   385         
       
   386         Tag.objects.update_tags(self.dead_parrot, 'foO bAr Ter')
       
   387         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   388         self.assertEquals(len(tags), 3)
       
   389         foo_tag = get_tag('foo')
       
   390         bar_tag = get_tag('bar')
       
   391         ter_tag = get_tag('ter')
       
   392         self.failUnless(foo_tag in tags)
       
   393         self.failUnless(bar_tag in tags)
       
   394         self.failUnless(ter_tag in tags)
       
   395         
       
   396         Tag.objects.update_tags(self.dead_parrot, 'foO bAr baZ')
       
   397         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   398         baz_tag = get_tag('baz')
       
   399         self.assertEquals(len(tags), 3)
       
   400         self.failUnless(bar_tag in tags)
       
   401         self.failUnless(baz_tag in tags)
       
   402         self.failUnless(foo_tag in tags)
       
   403         
       
   404         Tag.objects.add_tag(self.dead_parrot, 'FOO')
       
   405         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   406         self.assertEquals(len(tags), 3)
       
   407         self.failUnless(bar_tag in tags)
       
   408         self.failUnless(baz_tag in tags)
       
   409         self.failUnless(foo_tag in tags)
       
   410         
       
   411         Tag.objects.add_tag(self.dead_parrot, 'Zip')
       
   412         tags = Tag.objects.get_for_object(self.dead_parrot)
       
   413         self.assertEquals(len(tags), 4)
       
   414         zip_tag = get_tag('zip')
       
   415         self.failUnless(bar_tag in tags)
       
   416         self.failUnless(baz_tag in tags)
       
   417         self.failUnless(foo_tag in tags)
       
   418         self.failUnless(zip_tag in tags)
       
   419         
       
   420         f1 = FormTest.objects.create()
       
   421         f1.tags = u'TEST5'
       
   422         f1.save()
       
   423         tags = Tag.objects.get_for_object(f1)
       
   424         test5_tag = get_tag('test5')
       
   425         self.assertEquals(len(tags), 1)
       
   426         self.failUnless(test5_tag in tags)
       
   427         self.assertEquals(f1.tags, u'test5')
       
   428 
       
   429 class TestTagUsageForModelBaseCase(TestCase):
       
   430     def test_tag_usage_for_model_empty(self):
       
   431         self.assertEquals(Tag.objects.usage_for_model(Parrot), [])
       
   432 
       
   433 class TestTagUsageForModel(TestCase):
       
   434     def setUp(self):
       
   435         parrot_details = (
       
   436             ('pining for the fjords', 9, True,  'foo bar'),
       
   437             ('passed on',             6, False, 'bar baz ter'),
       
   438             ('no more',               4, True,  'foo ter'),
       
   439             ('late',                  2, False, 'bar ter'),
       
   440         )
       
   441         
       
   442         for state, perch_size, perch_smelly, tags in parrot_details:
       
   443             perch = Perch.objects.create(size=perch_size, smelly=perch_smelly)
       
   444             parrot = Parrot.objects.create(state=state, perch=perch)
       
   445             Tag.objects.update_tags(parrot, tags)
       
   446     
       
   447     def test_tag_usage_for_model(self):
       
   448         tag_usage = Tag.objects.usage_for_model(Parrot, counts=True)
       
   449         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   450         self.assertEquals(len(relevant_attribute_list), 4)
       
   451         self.failUnless((u'bar', 3) in relevant_attribute_list)
       
   452         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   453         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   454         self.failUnless((u'ter', 3) in relevant_attribute_list)
       
   455     
       
   456     def test_tag_usage_for_model_with_min_count(self):
       
   457         tag_usage = Tag.objects.usage_for_model(Parrot, min_count = 2)
       
   458         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   459         self.assertEquals(len(relevant_attribute_list), 3)
       
   460         self.failUnless((u'bar', 3) in relevant_attribute_list)
       
   461         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   462         self.failUnless((u'ter', 3) in relevant_attribute_list)
       
   463     
       
   464     def test_tag_usage_with_filter_on_model_objects(self):
       
   465         tag_usage = Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(state='no more'))
       
   466         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   467         self.assertEquals(len(relevant_attribute_list), 2)
       
   468         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   469         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   470         
       
   471         tag_usage = Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(state__startswith='p'))
       
   472         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   473         self.assertEquals(len(relevant_attribute_list), 4)
       
   474         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   475         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   476         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   477         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   478         
       
   479         tag_usage = Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(perch__size__gt=4))
       
   480         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   481         self.assertEquals(len(relevant_attribute_list), 4)
       
   482         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   483         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   484         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   485         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   486         
       
   487         tag_usage = Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(perch__smelly=True))
       
   488         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   489         self.assertEquals(len(relevant_attribute_list), 3)
       
   490         self.failUnless((u'bar', 1) in relevant_attribute_list)
       
   491         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   492         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   493         
       
   494         tag_usage = Tag.objects.usage_for_model(Parrot, min_count=2, filters=dict(perch__smelly=True))
       
   495         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   496         self.assertEquals(len(relevant_attribute_list), 1)
       
   497         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   498         
       
   499         tag_usage = Tag.objects.usage_for_model(Parrot, filters=dict(perch__size__gt=4))
       
   500         relevant_attribute_list = [(tag.name, hasattr(tag, 'counts')) for tag in tag_usage]
       
   501         self.assertEquals(len(relevant_attribute_list), 4)
       
   502         self.failUnless((u'bar', False) in relevant_attribute_list)
       
   503         self.failUnless((u'baz', False) in relevant_attribute_list)
       
   504         self.failUnless((u'foo', False) in relevant_attribute_list)
       
   505         self.failUnless((u'ter', False) in relevant_attribute_list)
       
   506         
       
   507         tag_usage = Tag.objects.usage_for_model(Parrot, filters=dict(perch__size__gt=99))
       
   508         relevant_attribute_list = [(tag.name, hasattr(tag, 'counts')) for tag in tag_usage]
       
   509         self.assertEquals(len(relevant_attribute_list), 0)
       
   510 
       
   511 class TestTagsRelatedForModel(TestCase):
       
   512     def setUp(self):
       
   513         parrot_details = (
       
   514             ('pining for the fjords', 9, True,  'foo bar'),
       
   515             ('passed on',             6, False, 'bar baz ter'),
       
   516             ('no more',               4, True,  'foo ter'),
       
   517             ('late',                  2, False, 'bar ter'),
       
   518         )
       
   519         
       
   520         for state, perch_size, perch_smelly, tags in parrot_details:
       
   521             perch = Perch.objects.create(size=perch_size, smelly=perch_smelly)
       
   522             parrot = Parrot.objects.create(state=state, perch=perch)
       
   523             Tag.objects.update_tags(parrot, tags)
       
   524             
       
   525     def test_related_for_model_with_tag_query_sets_as_input(self):
       
   526         related_tags = Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, counts=True)
       
   527         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   528         self.assertEquals(len(relevant_attribute_list), 3)
       
   529         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   530         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   531         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   532         
       
   533         related_tags = Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, min_count=2)
       
   534         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   535         self.assertEquals(len(relevant_attribute_list), 1)
       
   536         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   537         
       
   538         related_tags = Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, counts=False)
       
   539         relevant_attribute_list = [tag.name for tag in related_tags]
       
   540         self.assertEquals(len(relevant_attribute_list), 3)
       
   541         self.failUnless(u'baz' in relevant_attribute_list)
       
   542         self.failUnless(u'foo' in relevant_attribute_list)
       
   543         self.failUnless(u'ter' in relevant_attribute_list)
       
   544         
       
   545         related_tags = Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar', 'ter']), Parrot, counts=True)
       
   546         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   547         self.assertEquals(len(relevant_attribute_list), 1)
       
   548         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   549         
       
   550         related_tags = Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar', 'ter', 'baz']), Parrot, counts=True)
       
   551         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   552         self.assertEquals(len(relevant_attribute_list), 0)
       
   553         
       
   554     def test_related_for_model_with_tag_strings_as_input(self):
       
   555         # Once again, with feeling (strings)
       
   556         related_tags = Tag.objects.related_for_model('bar', Parrot, counts=True)
       
   557         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   558         self.assertEquals(len(relevant_attribute_list), 3)
       
   559         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   560         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   561         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   562         
       
   563         related_tags = Tag.objects.related_for_model('bar', Parrot, min_count=2)
       
   564         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   565         self.assertEquals(len(relevant_attribute_list), 1)
       
   566         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   567         
       
   568         related_tags = Tag.objects.related_for_model('bar', Parrot, counts=False)
       
   569         relevant_attribute_list = [tag.name for tag in related_tags]
       
   570         self.assertEquals(len(relevant_attribute_list), 3)
       
   571         self.failUnless(u'baz' in relevant_attribute_list)
       
   572         self.failUnless(u'foo' in relevant_attribute_list)
       
   573         self.failUnless(u'ter' in relevant_attribute_list)
       
   574         
       
   575         related_tags = Tag.objects.related_for_model(['bar', 'ter'], Parrot, counts=True)
       
   576         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   577         self.assertEquals(len(relevant_attribute_list), 1)
       
   578         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   579         
       
   580         related_tags = Tag.objects.related_for_model(['bar', 'ter', 'baz'], Parrot, counts=True)
       
   581         relevant_attribute_list = [(tag.name, tag.count) for tag in related_tags]
       
   582         self.assertEquals(len(relevant_attribute_list), 0)
       
   583         
       
   584 class TestGetTaggedObjectsByModel(TestCase):
       
   585     def setUp(self):
       
   586         parrot_details = (
       
   587             ('pining for the fjords', 9, True,  'foo bar'),
       
   588             ('passed on',             6, False, 'bar baz ter'),
       
   589             ('no more',               4, True,  'foo ter'),
       
   590             ('late',                  2, False, 'bar ter'),
       
   591         )
       
   592         
       
   593         for state, perch_size, perch_smelly, tags in parrot_details:
       
   594             perch = Perch.objects.create(size=perch_size, smelly=perch_smelly)
       
   595             parrot = Parrot.objects.create(state=state, perch=perch)
       
   596             Tag.objects.update_tags(parrot, tags)
       
   597             
       
   598         self.foo = Tag.objects.get(name='foo')
       
   599         self.bar = Tag.objects.get(name='bar')
       
   600         self.baz = Tag.objects.get(name='baz')
       
   601         self.ter = Tag.objects.get(name='ter')
       
   602         
       
   603         self.pining_for_the_fjords_parrot = Parrot.objects.get(state='pining for the fjords')
       
   604         self.passed_on_parrot = Parrot.objects.get(state='passed on')
       
   605         self.no_more_parrot = Parrot.objects.get(state='no more')
       
   606         self.late_parrot = Parrot.objects.get(state='late')
       
   607         
       
   608     def test_get_by_model_simple(self):
       
   609         parrots = TaggedItem.objects.get_by_model(Parrot, self.foo)
       
   610         self.assertEquals(len(parrots), 2)
       
   611         self.failUnless(self.no_more_parrot in parrots)
       
   612         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   613         
       
   614         parrots = TaggedItem.objects.get_by_model(Parrot, self.bar)
       
   615         self.assertEquals(len(parrots), 3)
       
   616         self.failUnless(self.late_parrot in parrots)
       
   617         self.failUnless(self.passed_on_parrot in parrots)
       
   618         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   619     
       
   620     def test_get_by_model_intersection(self):
       
   621         parrots = TaggedItem.objects.get_by_model(Parrot, [self.foo, self.baz])
       
   622         self.assertEquals(len(parrots), 0)
       
   623         
       
   624         parrots = TaggedItem.objects.get_by_model(Parrot, [self.foo, self.bar])
       
   625         self.assertEquals(len(parrots), 1)
       
   626         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   627         
       
   628         parrots = TaggedItem.objects.get_by_model(Parrot, [self.bar, self.ter])
       
   629         self.assertEquals(len(parrots), 2)
       
   630         self.failUnless(self.late_parrot in parrots)
       
   631         self.failUnless(self.passed_on_parrot in parrots)
       
   632         
       
   633         # Issue 114 - Intersection with non-existant tags
       
   634         parrots = TaggedItem.objects.get_intersection_by_model(Parrot, [])
       
   635         self.assertEquals(len(parrots), 0)
       
   636     
       
   637     def test_get_by_model_with_tag_querysets_as_input(self):
       
   638         parrots = TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['foo', 'baz']))
       
   639         self.assertEquals(len(parrots), 0)
       
   640         
       
   641         parrots = TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['foo', 'bar']))
       
   642         self.assertEquals(len(parrots), 1)
       
   643         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   644         
       
   645         parrots = TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['bar', 'ter']))
       
   646         self.assertEquals(len(parrots), 2)
       
   647         self.failUnless(self.late_parrot in parrots)
       
   648         self.failUnless(self.passed_on_parrot in parrots)
       
   649     
       
   650     def test_get_by_model_with_strings_as_input(self):
       
   651         parrots = TaggedItem.objects.get_by_model(Parrot, 'foo baz')
       
   652         self.assertEquals(len(parrots), 0)
       
   653         
       
   654         parrots = TaggedItem.objects.get_by_model(Parrot, 'foo bar')
       
   655         self.assertEquals(len(parrots), 1)
       
   656         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   657         
       
   658         parrots = TaggedItem.objects.get_by_model(Parrot, 'bar ter')
       
   659         self.assertEquals(len(parrots), 2)
       
   660         self.failUnless(self.late_parrot in parrots)
       
   661         self.failUnless(self.passed_on_parrot in parrots)
       
   662         
       
   663     def test_get_by_model_with_lists_of_strings_as_input(self):
       
   664         parrots = TaggedItem.objects.get_by_model(Parrot, ['foo', 'baz'])
       
   665         self.assertEquals(len(parrots), 0)
       
   666         
       
   667         parrots = TaggedItem.objects.get_by_model(Parrot, ['foo', 'bar'])
       
   668         self.assertEquals(len(parrots), 1)
       
   669         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   670         
       
   671         parrots = TaggedItem.objects.get_by_model(Parrot, ['bar', 'ter'])
       
   672         self.assertEquals(len(parrots), 2)
       
   673         self.failUnless(self.late_parrot in parrots)
       
   674         self.failUnless(self.passed_on_parrot in parrots)
       
   675     
       
   676     def test_get_by_nonexistent_tag(self):
       
   677         # Issue 50 - Get by non-existent tag
       
   678         parrots = TaggedItem.objects.get_by_model(Parrot, 'argatrons')
       
   679         self.assertEquals(len(parrots), 0)
       
   680     
       
   681     def test_get_union_by_model(self):
       
   682         parrots = TaggedItem.objects.get_union_by_model(Parrot, ['foo', 'ter'])
       
   683         self.assertEquals(len(parrots), 4)
       
   684         self.failUnless(self.late_parrot in parrots)
       
   685         self.failUnless(self.no_more_parrot in parrots)
       
   686         self.failUnless(self.passed_on_parrot in parrots)
       
   687         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   688         
       
   689         parrots = TaggedItem.objects.get_union_by_model(Parrot, ['bar', 'baz'])
       
   690         self.assertEquals(len(parrots), 3)
       
   691         self.failUnless(self.late_parrot in parrots)
       
   692         self.failUnless(self.passed_on_parrot in parrots)
       
   693         self.failUnless(self.pining_for_the_fjords_parrot in parrots)
       
   694         
       
   695         # Issue 114 - Union with non-existant tags
       
   696         parrots = TaggedItem.objects.get_union_by_model(Parrot, [])
       
   697         self.assertEquals(len(parrots), 0)
       
   698 
       
   699 class TestGetRelatedTaggedItems(TestCase):
       
   700     def setUp(self):
       
   701         parrot_details = (
       
   702             ('pining for the fjords', 9, True,  'foo bar'),
       
   703             ('passed on',             6, False, 'bar baz ter'),
       
   704             ('no more',               4, True,  'foo ter'),
       
   705             ('late',                  2, False, 'bar ter'),
       
   706         )
       
   707         
       
   708         for state, perch_size, perch_smelly, tags in parrot_details:
       
   709             perch = Perch.objects.create(size=perch_size, smelly=perch_smelly)
       
   710             parrot = Parrot.objects.create(state=state, perch=perch)
       
   711             Tag.objects.update_tags(parrot, tags)
       
   712             
       
   713         self.l1 = Link.objects.create(name='link 1')
       
   714         Tag.objects.update_tags(self.l1, 'tag1 tag2 tag3 tag4 tag5')
       
   715         self.l2 = Link.objects.create(name='link 2')
       
   716         Tag.objects.update_tags(self.l2, 'tag1 tag2 tag3')
       
   717         self.l3 = Link.objects.create(name='link 3')
       
   718         Tag.objects.update_tags(self.l3, 'tag1')
       
   719         self.l4 = Link.objects.create(name='link 4')
       
   720         
       
   721         self.a1 = Article.objects.create(name='article 1')
       
   722         Tag.objects.update_tags(self.a1, 'tag1 tag2 tag3 tag4')
       
   723     
       
   724     def test_get_related_objects_of_same_model(self):
       
   725         related_objects = TaggedItem.objects.get_related(self.l1, Link)
       
   726         self.assertEquals(len(related_objects), 2)
       
   727         self.failUnless(self.l2 in related_objects)
       
   728         self.failUnless(self.l3 in related_objects)
       
   729         
       
   730         related_objects = TaggedItem.objects.get_related(self.l4, Link)
       
   731         self.assertEquals(len(related_objects), 0)
       
   732     
       
   733     def test_get_related_objects_of_same_model_limited_number_of_results(self):
       
   734         # This fails on Oracle because it has no support for a 'LIMIT' clause.
       
   735         # See http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:127412348064
       
   736         
       
   737         # ask for no more than 1 result
       
   738         related_objects = TaggedItem.objects.get_related(self.l1, Link, num=1)
       
   739         self.assertEquals(len(related_objects), 1)
       
   740         self.failUnless(self.l2 in related_objects)
       
   741         
       
   742     def test_get_related_objects_of_same_model_limit_related_items(self):
       
   743         related_objects = TaggedItem.objects.get_related(self.l1, Link.objects.exclude(name='link 3'))
       
   744         self.assertEquals(len(related_objects), 1)
       
   745         self.failUnless(self.l2 in related_objects)
       
   746     
       
   747     def test_get_related_objects_of_different_model(self):
       
   748         related_objects = TaggedItem.objects.get_related(self.a1, Link)
       
   749         self.assertEquals(len(related_objects), 3)
       
   750         self.failUnless(self.l1 in related_objects)
       
   751         self.failUnless(self.l2 in related_objects)
       
   752         self.failUnless(self.l3 in related_objects)
       
   753             
       
   754         Tag.objects.update_tags(self.a1, 'tag6')
       
   755         related_objects = TaggedItem.objects.get_related(self.a1, Link)
       
   756         self.assertEquals(len(related_objects), 0)
       
   757         
       
   758 class TestTagUsageForQuerySet(TestCase):
       
   759     def setUp(self):
       
   760         parrot_details = (
       
   761             ('pining for the fjords', 9, True,  'foo bar'),
       
   762             ('passed on',             6, False, 'bar baz ter'),
       
   763             ('no more',               4, True,  'foo ter'),
       
   764             ('late',                  2, False, 'bar ter'),
       
   765         )
       
   766         
       
   767         for state, perch_size, perch_smelly, tags in parrot_details:
       
   768             perch = Perch.objects.create(size=perch_size, smelly=perch_smelly)
       
   769             parrot = Parrot.objects.create(state=state, perch=perch)
       
   770             Tag.objects.update_tags(parrot, tags)
       
   771     
       
   772     def test_tag_usage_for_queryset(self):
       
   773         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(state='no more'), counts=True)
       
   774         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   775         self.assertEquals(len(relevant_attribute_list), 2)
       
   776         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   777         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   778         
       
   779         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(state__startswith='p'), counts=True)
       
   780         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   781         self.assertEquals(len(relevant_attribute_list), 4)
       
   782         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   783         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   784         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   785         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   786         
       
   787         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(perch__size__gt=4), counts=True)
       
   788         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   789         self.assertEquals(len(relevant_attribute_list), 4)
       
   790         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   791         self.failUnless((u'baz', 1) in relevant_attribute_list)
       
   792         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   793         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   794         
       
   795         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(perch__smelly=True), counts=True)
       
   796         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   797         self.assertEquals(len(relevant_attribute_list), 3)
       
   798         self.failUnless((u'bar', 1) in relevant_attribute_list)
       
   799         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   800         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   801         
       
   802         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(perch__smelly=True), min_count=2)
       
   803         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   804         self.assertEquals(len(relevant_attribute_list), 1)
       
   805         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   806         
       
   807         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(perch__size__gt=4))
       
   808         relevant_attribute_list = [(tag.name, hasattr(tag, 'counts')) for tag in tag_usage]
       
   809         self.assertEquals(len(relevant_attribute_list), 4)
       
   810         self.failUnless((u'bar', False) in relevant_attribute_list)
       
   811         self.failUnless((u'baz', False) in relevant_attribute_list)
       
   812         self.failUnless((u'foo', False) in relevant_attribute_list)
       
   813         self.failUnless((u'ter', False) in relevant_attribute_list)
       
   814         
       
   815         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(perch__size__gt=99))
       
   816         relevant_attribute_list = [(tag.name, hasattr(tag, 'counts')) for tag in tag_usage]
       
   817         self.assertEquals(len(relevant_attribute_list), 0)
       
   818         
       
   819         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(Q(perch__size__gt=6) | Q(state__startswith='l')), counts=True)
       
   820         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   821         self.assertEquals(len(relevant_attribute_list), 3)
       
   822         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   823         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   824         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   825         
       
   826         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(Q(perch__size__gt=6) | Q(state__startswith='l')), min_count=2)
       
   827         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   828         self.assertEquals(len(relevant_attribute_list), 1)
       
   829         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   830         
       
   831         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.filter(Q(perch__size__gt=6) | Q(state__startswith='l')))
       
   832         relevant_attribute_list = [(tag.name, hasattr(tag, 'counts')) for tag in tag_usage]
       
   833         self.assertEquals(len(relevant_attribute_list), 3)
       
   834         self.failUnless((u'bar', False) in relevant_attribute_list)
       
   835         self.failUnless((u'foo', False) in relevant_attribute_list)
       
   836         self.failUnless((u'ter', False) in relevant_attribute_list)
       
   837         
       
   838         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.exclude(state='passed on'), counts=True)
       
   839         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   840         self.assertEquals(len(relevant_attribute_list), 3)
       
   841         self.failUnless((u'bar', 2) in relevant_attribute_list)
       
   842         self.failUnless((u'foo', 2) in relevant_attribute_list)
       
   843         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   844         
       
   845         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.exclude(state__startswith='p'), min_count=2)
       
   846         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   847         self.assertEquals(len(relevant_attribute_list), 1)
       
   848         self.failUnless((u'ter', 2) in relevant_attribute_list)
       
   849         
       
   850         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.exclude(Q(perch__size__gt=6) | Q(perch__smelly=False)), counts=True)
       
   851         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   852         self.assertEquals(len(relevant_attribute_list), 2)
       
   853         self.failUnless((u'foo', 1) in relevant_attribute_list)
       
   854         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   855         
       
   856         tag_usage = Tag.objects.usage_for_queryset(Parrot.objects.exclude(perch__smelly=True).filter(state__startswith='l'), counts=True)
       
   857         relevant_attribute_list = [(tag.name, tag.count) for tag in tag_usage]
       
   858         self.assertEquals(len(relevant_attribute_list), 2)
       
   859         self.failUnless((u'bar', 1) in relevant_attribute_list)
       
   860         self.failUnless((u'ter', 1) in relevant_attribute_list)
       
   861         
       
   862 ################
       
   863 # Model Fields #
       
   864 ################
       
   865 
       
   866 class TestTagFieldInForms(TestCase):
       
   867     def test_tag_field_in_modelform(self):
       
   868         # Ensure that automatically created forms use TagField
       
   869         class TestForm(forms.ModelForm):
       
   870             class Meta:
       
   871                 model = FormTest
       
   872                 
       
   873         form = TestForm()
       
   874         self.assertEquals(form.fields['tags'].__class__.__name__, 'TagField')
       
   875     
       
   876     def test_recreation_of_tag_list_string_representations(self):
       
   877         plain = Tag.objects.create(name='plain')
       
   878         spaces = Tag.objects.create(name='spa ces')
       
   879         comma = Tag.objects.create(name='com,ma')
       
   880         self.assertEquals(edit_string_for_tags([plain]), u'plain')
       
   881         self.assertEquals(edit_string_for_tags([plain, spaces]), u'plain, spa ces')
       
   882         self.assertEquals(edit_string_for_tags([plain, spaces, comma]), u'plain, spa ces, "com,ma"')
       
   883         self.assertEquals(edit_string_for_tags([plain, comma]), u'plain "com,ma"')
       
   884         self.assertEquals(edit_string_for_tags([comma, spaces]), u'"com,ma", spa ces')
       
   885     
       
   886     def test_tag_d_validation(self):
       
   887         t = TagField()
       
   888         self.assertEquals(t.clean('foo'), u'foo')
       
   889         self.assertEquals(t.clean('foo bar baz'), u'foo bar baz')
       
   890         self.assertEquals(t.clean('foo,bar,baz'), u'foo,bar,baz')
       
   891         self.assertEquals(t.clean('foo, bar, baz'), u'foo, bar, baz')
       
   892         self.assertEquals(t.clean('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvb bar'),
       
   893             u'foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvb bar')
       
   894         try:
       
   895             t.clean('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbn bar')
       
   896         except forms.ValidationError, ve:
       
   897             self.assertEquals(str(ve), "[u'Each tag may be no more than 50 characters long.']")
       
   898         except Exception, e:
       
   899             raise e
       
   900         else:
       
   901             raise self.failureException('a ValidationError exception was supposed to have been raised.')