[damned-lies] Added loads of Python 3 compatibility fixes



commit 643f1d79fbf0003692b1c2bf16e36524838576e3
Author: Claude Paroz <claude 2xlibre net>
Date:   Fri Nov 13 15:51:28 2015 +0100

    Added loads of Python 3 compatibility fixes

 common/fields.py                       |    7 ++--
 common/templatetags/list_to_columns.py |    6 ++--
 common/utils.py                        |   16 +++++-----
 damnedlies/settings.py                 |    2 +-
 languages/views.py                     |    4 +-
 people/forms.py                        |    6 ++--
 people/templatetags/people.py          |    2 +-
 people/tests.py                        |    2 +-
 stats/doap.py                          |    2 +-
 stats/models.py                        |   54 ++++++++++---------------------
 stats/tests/tests.py                   |   15 +++++----
 stats/utils.py                         |   16 +++++----
 stats/views.py                         |    4 +-
 teams/tests.py                         |    2 +-
 vertimus/tests/tests.py                |   20 ++++++-----
 15 files changed, 73 insertions(+), 85 deletions(-)
---
diff --git a/common/fields.py b/common/fields.py
index 80ba9bf..25077ee 100644
--- a/common/fields.py
+++ b/common/fields.py
@@ -6,6 +6,7 @@ from django import forms
 from django.core import exceptions
 from django.core.serializers.json import DjangoJSONEncoder
 from django.db import models
+from django.utils import six
 
 
 class DictionaryField(models.Field):
@@ -19,7 +20,7 @@ class DictionaryField(models.Field):
             return None
         elif value == "":
             return {}
-        elif isinstance(value, basestring):
+        elif isinstance(value, six.string_types):
             try:
                 return dict(json.loads(value))
             except (ValueError, TypeError):
@@ -33,7 +34,7 @@ class DictionaryField(models.Field):
     def get_prep_value(self, value):
         if not value:
             return ""
-        if isinstance(value, basestring):
+        if isinstance(value, six.string_types):
             return value
         else:
             return json.dumps(value)
@@ -65,7 +66,7 @@ class JSONField(models.TextField):
             return None
 
         try:
-            if isinstance(value, basestring):
+            if isinstance(value, six.string_types):
                 return json.loads(value)
         except ValueError:
             pass
diff --git a/common/templatetags/list_to_columns.py b/common/templatetags/list_to_columns.py
index 0c8f70b..75b1c9e 100644
--- a/common/templatetags/list_to_columns.py
+++ b/common/templatetags/list_to_columns.py
@@ -30,7 +30,7 @@ class SplitListNode(template.Node):
 
     def split_seq(self, list, cols=2):
         start = 0
-        for i in xrange(cols):
+        for i in range(cols):
             stop = start + len(list[i::cols])
             yield list[start:stop]
             start = stop
@@ -43,8 +43,8 @@ def list_to_columns(parser, token):
     """Parse template tag: {% list_to_columns list as new_list 2 %}"""
     bits = token.contents.split()
     if len(bits) != 5:
-        raise template.TemplateSyntaxError, "list_to_columns list as new_list 2"
+        raise template.TemplateSyntaxError("list_to_columns list as new_list 2")
     if bits[2] != 'as':
-        raise template.TemplateSyntaxError, "second argument to the list_to_columns tag must be 'as'"
+        raise template.TemplateSyntaxError("second argument to the list_to_columns tag must be 'as'")
     return SplitListNode(bits[1], bits[4], bits[3])
 list_to_columns = register.tag(list_to_columns)
diff --git a/common/utils.py b/common/utils.py
index 6e65bad..2a5ebc2 100644
--- a/common/utils.py
+++ b/common/utils.py
@@ -129,41 +129,41 @@ def imerge_sorted_by_field(object_list1, object_list2, field):
     # StopIteration to find the source.
 
     try:
-        el1 = iter1.next()
+        el1 = next(iter1)
     except StopIteration:
         # Finish the other list
         while True:
-            el2 = iter2.next()
+            el2 = next(iter2)
             yield el2
 
     try:
-        el2 = iter2.next()
+        el2 = next(iter2)
     except StopIteration:
         # Finish the other list
         while True:
             # el1 is already fetched
             yield el1
-            el1 = iter1.next()
+            el1 = next(iter1)
 
     while True:
         if op(getattr(el1, field), getattr(el2, field)):
             yield el1
             try:
-                el1 = iter1.next()
+                el1 = next(iter1)
             except StopIteration:
                 # Finish the other list
                 while True:
                     yield el2
-                    el2 = iter2.next()
+                    el2 = next(iter2)
         else:
             yield el2
             try:
-                el2 = iter2.next()
+                el2 = next(iter2)
             except StopIteration:
                 # Finish the other list
                 while True:
                     yield el1
-                    el1 = iter1.next()
+                    el1 = next(iter1)
 
 def is_site_admin(user):
     return user.is_superuser or settings.ADMIN_GROUP in [g.name for g in user.groups.all()]
diff --git a/damnedlies/settings.py b/damnedlies/settings.py
index 73c6e95..f9a8f60 100644
--- a/damnedlies/settings.py
+++ b/damnedlies/settings.py
@@ -86,7 +86,7 @@ VCS_HOME_WARNING = gettext_noop(u"This module is not part of the GNOME Git repos
 # By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings
 UPLOAD_DIR = 'upload'
 UPLOAD_ARCHIVED_DIR = 'upload-archived'
-FILE_UPLOAD_PERMISSIONS = 0600
+FILE_UPLOAD_PERMISSIONS = 0o600
 
 LOGIN_URL = '/login/'
 
diff --git a/languages/views.py b/languages/views.py
index 971ccb0..a46a89e 100644
--- a/languages/views.py
+++ b/languages/views.py
@@ -128,13 +128,13 @@ def language_release_xml(request, locale, release_name):
         content += "<untranslated>%s</untranslated>" % categ['catuntrans']
         # Modules
         for module, mod_stats in categ['modules'].items():
-            branch_name, domains = mod_stats.items()[0]
+            branch_name, domains = list(mod_stats.items())[0]
             content += "<module id=\"%s\" branch=\"%s\">" % (module.name, branch_name)
             # DOC domains
             if catname in stats['doc']['categs'] and stats['doc']['categs'][catname]['modules']:
                 for docmod, data in stats['doc']['categs'][catname]['modules'].items():
                     if docmod == module:
-                        content += get_domain_stats(data.values()[0], "document")
+                        content += get_domain_stats(list(data.values())[0], "document")
             # UI stats
             content += get_domain_stats(domains, "domain")
             content += "</module>"
diff --git a/people/forms.py b/people/forms.py
index 5ca6f8e..e95e481 100644
--- a/people/forms.py
+++ b/people/forms.py
@@ -70,8 +70,8 @@ class RegistrationForm(forms.Form):
         if openid:
             from django_openid_auth.models import UserOpenID
             user_oid = UserOpenID.objects.create(user=new_user, claimed_id=openid)
-        salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
-        activation_key = hashlib.sha1(salt+force_bytes(username)).hexdigest()
+        salt = hashlib.sha1(force_bytes(random.random())).hexdigest()[:5]
+        activation_key = hashlib.sha1(force_bytes(salt + username)).hexdigest()
         new_user.activation_key = activation_key
         new_user.is_active = False
         new_user.save()
@@ -132,7 +132,7 @@ def get_image_size(url):
             if p.image:
                 size = p.image.size
                 break
-    except Exception, e:
+    except Exception as e:
         raise forms.ValidationError(u"Sorry, an error occurred while trying to get image size (%s)" % str(e))
     file.close()
     if not size:
diff --git a/people/templatetags/people.py b/people/templatetags/people.py
index fac0ada..da3a5b8 100644
--- a/people/templatetags/people.py
+++ b/people/templatetags/people.py
@@ -32,7 +32,7 @@ def people_image(person):
             '<img src="https://secure.gravatar.com/avatar/{hash}.jpg?{qs}"; alt="gravatar icon">',
             hash=digest,
             # Size, default image, rating
-            qs=urlencode({'s': '80', 'd': 'identicon', 'r': 'g'}),
+            qs=urlencode([('s', '80'), ('d', 'identicon'), ('r', 'g')]),
         )
     elif person.image:
         tag = format_html(
diff --git a/people/tests.py b/people/tests.py
index 71e8aa5..1de6d98 100644
--- a/people/tests.py
+++ b/people/tests.py
@@ -152,7 +152,7 @@ class PeopleTestCase(TestCase):
         pn.use_gravatar = True
         self.assertHTMLEqual(
             people.people_image(pn),
-            '<img alt="gravatar icon" 
src="https://secure.gravatar.com/avatar/618b8b6c1c973c780ec218242c49cbe7.jpg?s=80&r=g&d=identicon"; />'
+            '<img alt="gravatar icon" 
src="https://secure.gravatar.com/avatar/618b8b6c1c973c780ec218242c49cbe7.jpg?s=80&d=identicon&r=g"; />'
         )
 
     @skipUnless(pyicu_present, "PyICU package is required for this test")
diff --git a/stats/doap.py b/stats/doap.py
index cd24b03..00fcfbc 100644
--- a/stats/doap.py
+++ b/stats/doap.py
@@ -2,9 +2,9 @@
 import os
 import re
 from xml.etree.ElementTree import parse
-from urllib import unquote
 
 from django.utils.encoding import force_text
+from django.utils.six.moves.urllib.parse import unquote
 
 from people.models import Person
 
diff --git a/stats/models.py b/stats/models.py
index d47f60a..cb90c30 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -29,14 +29,15 @@ import threading
 from datetime import datetime
 from functools import total_ordering
 from time import sleep
-from urllib2 import URLError
 
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.core.validators import RegexValidator
-from django.utils.encoding import python_2_unicode_compatible
+from django.utils.encoding import force_text, python_2_unicode_compatible
 from django.utils.functional import cached_property
+from django.utils.six.moves.urllib.error import URLError
+from django.utils.six.moves.urllib import request
 from django.utils.translation import ungettext, ugettext as _, ugettext_noop
 from django.utils import dateformat
 from django.db import models
@@ -406,21 +407,10 @@ class Branch(models.Model):
                     stats[domain].append(FakeLangStatistics(stats[domain][0], lang))
         # Sort
         for key, doms in stats.items():
-            doms.sort(self.compare_stats)
+            #Sort stats, pot first, then translated (desc), then language name
+            doms.sort(key=lambda st:(int(st.language_id is not None), -st.translated(), st.get_lang()))
         return stats
 
-    def compare_stats(self, a, b):
-        """ Sort stats, pot first, then translated (desc), then language name """
-        if not a.language:
-            return -1
-        elif not b.language:
-            return 1
-        else:
-            res = -cmp(a.translated(), b.translated())
-            if not res:
-                res = cmp(a.get_lang(), b.get_lang())
-        return res
-
     def get_doc_stats(self, mandatory_langs=[]):
         if not self._doc_stats:
             self._doc_stats = self.get_stats('doc', mandatory_langs)
@@ -718,7 +708,7 @@ class Branch(models.Model):
             stat.update_stats(dest_path)
         else:
             self.update_stats(force=False, checkout=False, domain=domain)
-        return commit_hash
+        return force_text(commit_hash)
 
     def cherrypick_commit(self, commit_hash, domain):
         """
@@ -845,14 +835,13 @@ class Domain(models.Model):
             pot_command.extend(['--msgid-bugs-address', self.module.get_bugs_enter_url()])
         elif self.pot_method.startswith(('http://', 'https://')):
             # Get POT from URL and save file locally
-            import urllib2
-            req = urllib2.Request(self.pot_method)
+            req = request.Request(self.pot_method)
             try:
-                handle = urllib2.urlopen(req)
+                handle = request.urlopen(req)
             except URLError:
                 return "", (("error", ugettext_noop("Error retrieving pot file from URL.")),)
             potfile = os.path.join(vcs_path, self.potbase() + ".pot")
-            with open(potfile, 'w') as f:
+            with open(potfile, 'wb') as f:
                 f.write(handle.read())
             pot_command = ':'  # noop
         elif self.module.name == 'damned-lies':
@@ -882,7 +871,7 @@ class Domain(models.Model):
             return "", (("error", ugettext_noop("Error regenerating POT file for 
%(file)s:\n<pre>%(cmd)s\n%(output)s</pre>")
                                  % {'file': self.potbase(),
                                     'cmd': ' '.join(pot_command) if isinstance(pot_command, list) else 
pot_command,
-                                    'output': errs.decode('utf-8')}),
+                                    'output': force_text(errs)}),
                        )
         else:
             return potfile, ()
@@ -985,7 +974,7 @@ class Release(models.Model):
         def perc(x, y):
             return int(x / y * 100)
         for k in stats.keys():
-            stats[k]['stats'] = map(perc, stats[k]['stats'], totals)
+            stats[k]['stats'] = list(map(perc, stats[k]['stats'], totals))
             stats[k]['diff'] = stats[k]['stats'][-1] - stats[k]['stats'][0]
         return stats
 
@@ -1137,18 +1126,11 @@ class Release(models.Model):
                     stats[locale]['ui_part']['fuzzy_perc'] = int(100 * row['fuzzy_p'] 
/total_uistrings_part[locale])
                     stats[locale]['ui_part']['untranslated_perc'] = int(100 * 
stats[locale]['ui_part']['untranslated'] / total_uistrings_part[locale])
 
-        results = stats.values()
-        results.sort(self.compare_stats)
+        results = list(stats.values())
+        # Sort by most translated first
+        results.sort(key=lambda st: (-st['ui']['translated'], -st['doc']['translated'], st['lang_name']))
         return results
 
-    def compare_stats(self, a, b):
-        res = cmp(b['ui']['translated'], a['ui']['translated'])
-        if not res:
-            res = cmp(b['doc']['translated'], a['doc']['translated'])
-            if not res:
-                res = cmp(b['lang_name'], a['lang_name'])
-        return res
-
     def get_lang_stats(self, lang):
         """ Get statistics for a specific language, producing the stats data structure
             Used for displaying the language-release template """
@@ -1348,7 +1330,7 @@ class Statistics(models.Model):
         return False
 
     def is_pot_stats(self):
-        return self.language is None
+        return self.language_id is None
 
     def tr_percentage(self, scope='full'):
         if scope == 'full' and self.full_po:
@@ -1718,17 +1700,17 @@ class Statistics(models.Model):
             stats['categs'][categ_key]['catuntrans'] += stat.untranslated(scope)
             if module not in stats['categs'][categ_key]['modules']:
                 # first element is a placeholder for a fake stat
-                stats['categs'][categ_key]['modules'][module] = {branchname:[[' fake', None], (domname, 
stat)]}
+                stats['categs'][categ_key]['modules'][module] = {branchname:[[' fake', None], [domname, 
stat]]}
             elif branchname not in stats['categs'][categ_key]['modules'][module]:
                 # first element is a placeholder for a fake stat
-                stats['categs'][categ_key]['modules'][module][branchname] = [[' fake', None], (domname, 
stat)]
+                stats['categs'][categ_key]['modules'][module][branchname] = [[' fake', None], [domname, 
stat]]
             else:
                 # Here we add the 2nd or more stat to the same module-branch
                 if len(stats['categs'][categ_key]['modules'][module][branchname]) == 2:
                     # Create a fake statistics object for module summary
                     stats['categs'][categ_key]['modules'][module][branchname][0][1] = 
FakeSummaryStatistics(stat.domain.module, stat.branch, dtype)
                     
stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stats['categs'][categ_key]['modules'][module][branchname][1][1])
-                stats['categs'][categ_key]['modules'][module][branchname].append((domname, stat))
+                stats['categs'][categ_key]['modules'][module][branchname].append([domname, stat])
                 stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stat)
 
         # Compute percentages and sorting
diff --git a/stats/tests/tests.py b/stats/tests/tests.py
index 0d0add2..06b9b84 100644
--- a/stats/tests/tests.py
+++ b/stats/tests/tests.py
@@ -32,11 +32,12 @@ from django.core.exceptions import ValidationError
 from django.core.urlresolvers import reverse
 from django.test import TestCase
 
-from stats.models import Module, Domain, Branch, Release, Statistics, FakeLangStatistics
+from stats.models import (
+    Module, Domain, Branch, Release, CategoryName, Statistics, FakeLangStatistics,
+)
 from stats import utils
 from languages.models import Language
 
-from fixture_factory import *
 
 def test_scratchdir(test_func):
     """ Decorator to temporarily use the scratchdir inside the test directory """
@@ -119,7 +120,7 @@ class ModuleTestCase(TestCase):
         self.assertContains(response, '<li><a href="/module/zenity/">zenity</a></li>')
         # Also test JSON output
         response = self.client.get(reverse('modules', args=['json']))
-        content = json.loads(response.content)
+        content = json.loads(response.content.decode('utf-8'))
         self.assertEqual(len(content), Module.objects.count())
         self.assertEqual(content[-1]["fields"]["vcs_root"], 'git://git.gnome.org/zenity')
 
@@ -281,7 +282,7 @@ class ModuleTestCase(TestCase):
         self.assertContains(response, "Language-Team: Tamil <ta li org>")
         try:
             response = self.client.get('/module/po/gnome-hello/po/master/ta-reduced.po')
-        except OSError, e:
+        except OSError as e:
             if "msginit" in str(e):
                 self.fail(
                     "You are probably running an unpatched version of the translate-toolkit. "
@@ -289,7 +290,7 @@ class ModuleTestCase(TestCase):
                 )
                 return
             else:
-                raise e
+                raise
         self.assertContains(response, """# Tamil translation for gnome-hello.""")
 
     @test_scratchdir
@@ -457,7 +458,7 @@ class StatisticsTests(TestCase):
     def test_total_by_releases(self):
         releases = list(Release.objects.filter(name__in=('gnome-3-8', 'gnome-dev')))
         stats = Release.total_by_releases('ui', releases)
-        self.assertEqual(list(stats.keys()), ['fr', 'it'])
+        self.assertEqual(set(stats.keys()), {'fr', 'it'})
         self.assertEqual(stats['fr']['diff'], 0)
         self.assertEqual(stats['fr']['stats'], [100, 100])
         self.assertEqual(stats['it']['diff'], 24)
@@ -569,7 +570,7 @@ class OtherTests(TestCase):
             self.assertContains(response, item)
         # Also test JSON output
         response = self.client.get(reverse('releases', args=['json']))
-        content = json.loads(response.content)
+        content = json.loads(response.content.decode('utf-8'))
         self.assertEqual(content[0]["fields"]["name"], "gnome-3-18")
 
     def test_bugzilla_linkify(self):
diff --git a/stats/utils.py b/stats/utils.py
index 2a25093..4a284a3 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -38,9 +38,10 @@ from django.core.mail import send_mail
 from django.template import Context
 from django.template.loader import get_template
 from django.utils import six
+from django.utils.encoding import force_bytes, force_text
 from django.utils.translation import ugettext_noop
 
-import potdiff
+from . import potdiff
 
 STATUS_OK = 0
 
@@ -139,8 +140,8 @@ def run_shell_command(cmd, input_data=None, raise_on_error=False, **popen_kwargs
     pipe = Popen(cmd, shell=shell, stdin=stdin, stdout=PIPE, stderr=PIPE, **popen_kwargs)
     if input_data:
         try:
-            pipe.stdin.write(input_data)
-        except IOError, e:
+            pipe.stdin.write(force_bytes(input_data))
+        except IOError as e:
             if e.errno != errno.EPIPE:
                 raise
     (output, errout) = pipe.communicate()
@@ -332,7 +333,7 @@ def po_file_stats(pofile, msgfmt_checks=True):
         }
     c_env = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
 
-    if isinstance(pofile, basestring):
+    if isinstance(pofile, six.string_types):
         # pofile is a filesystem path
         filename = os.path.basename(pofile)
         if not os.access(pofile, os.R_OK):
@@ -372,6 +373,7 @@ def po_file_stats(pofile, msgfmt_checks=True):
         res['errors'].append(("warn", ugettext_noop("This PO file has an executable bit set.")))
 
     # msgfmt output stats on stderr
+    errs = force_text(errs)
     r_tr = re.search(r"([0-9]+) translated", errs)
     r_un = re.search(r"([0-9]+) untranslated", errs)
     r_fz = re.search(r"([0-9]+) fuzzy", errs)
@@ -471,7 +473,7 @@ def get_fig_stats(pofile, doc_format):
     if status != STATUS_OK:
         # FIXME: something should be logged here
         return []
-    lines = output.split('\n')
+    lines = output.decode('utf-8').split('\n')
     while lines[0][0] != "#":
         lines = lines[1:] # skip warning messages at the top of the output
 
@@ -538,7 +540,7 @@ def copy_file(file1, file2):
 def compute_md5(full_path):
     m = hashlib.md5()
     block_size=2**13
-    with open(full_path) as fh:
+    with open(full_path, 'rb') as fh:
         while True:
             data = fh.read(block_size)
             if not data:
@@ -554,7 +556,7 @@ def notify_list(branch, diff):
     text = template.render(Context({
         'module' : '%s.%s' % (branch.module.name, branch.name),
         'ourweb' : current_site.domain,
-        'potdiff' : "\n    ".join(diff).decode('utf-8'),
+        'potdiff' : force_text("\n    ".join(diff)),
         'commit_log': branch.get_vcs_web_log_url(),
     }))
     send_mail(subject="String additions to '%s.%s'" % (branch.module.name, branch.name),
diff --git a/stats/views.py b/stats/views.py
index 04472b0..b62cdff 100644
--- a/stats/views.py
+++ b/stats/views.py
@@ -141,7 +141,7 @@ def module_edit_branches(request, module_name):
                             messages.success(request, "The new branch %s has been added" % branch_name)
                             updated = True
                             # Send message to gnome-i18n?
-                        except Exception, e:
+                        except Exception as e:
                             messages.error(request, "Error adding the branch '%s': %s" % (branch_name, 
str(e)))
                             branch = None
             else:
@@ -222,7 +222,7 @@ def dynamic_po(request, module_name, domain, branch_name, filename):
         raise Http404
     potfile = get_object_or_404(Statistics, branch=branch, domain=domain, language=None)
 
-    file_path = potfile.po_path(reduced=reduced).encode('ascii')
+    file_path = potfile.po_path(reduced=reduced)
     if not os.access(file_path, os.R_OK):
         raise Http404
 
diff --git a/teams/tests.py b/teams/tests.py
index 79fbed7..57de6fb 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -220,7 +220,7 @@ class JSONTeamsTest(TeamsAndRolesTests):
                 "coordinators": []
             }
             ]"""
-        self.assertJSONEqual(response.content, expected_JSON)
+        self.assertJSONEqual(response.content.decode('utf-8'), expected_JSON)
 
 
 class RoleTest(TeamsAndRolesTests):
diff --git a/vertimus/tests/tests.py b/vertimus/tests/tests.py
index bb5e9e5..9ddd0e0 100644
--- a/vertimus/tests/tests.py
+++ b/vertimus/tests/tests.py
@@ -18,6 +18,7 @@
 # along with this program; if not, see <http://www.gnu.org/licenses/>.
 
 import os
+from xml.dom.minidom import parseString
 
 from django.conf import settings
 from django.core.files.base import File, ContentFile
@@ -548,16 +549,16 @@ class VertimusTest(TeamsAndRolesTests):
     def test_uploaded_file_validation(self):
         # Test a non valid po file
         post_content = QueryDict('action=WC&comment=Test1')
-        post_file = MultiValueDict({'file': [SimpleUploadedFile('filename.po', 'Not valid po file 
content')]})
+        post_file = MultiValueDict({'file': [SimpleUploadedFile('filename.po', b'Not valid po file 
content')]})
         form = ActionForm(None, [ActionWC()], True, post_content, post_file)
 
         self.assertTrue('file' in form.errors)
 
         # Test a valid po file
-        f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'r')
-        post_file = MultiValueDict({'file': [File(f)]})
-        form = ActionForm(None, [ActionWC()], True, post_content, post_file)
-        self.assertTrue(form.is_valid())
+        with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'rb') as fh:
+            post_file = MultiValueDict({'file': [File(fh)]})
+            form = ActionForm(None, [ActionWC()], True, post_content, post_file)
+            self.assertTrue(form.is_valid())
 
         # Test form without file
         form = ActionForm(None, [ActionWC()], True, post_content)
@@ -571,11 +572,12 @@ class VertimusTest(TeamsAndRolesTests):
         action.apply_on(state, {'send_to_ml': action.send_mail_to_ml, 'comment': "Translating"})
 
         response = self.client.get(reverse('lang_feed', args=[self.l.locale]))
-        self.assertContains(response,
-            """<rss xmlns:atom="http://www.w3.org/2005/Atom"; version="2.0">""")
-        self.assertContains(response,
+        doc = parseString(response.content)
+        self.assertEqual(doc.childNodes[0].tagName, 'rss')
+        self.assertEqual(doc.childNodes[0].getAttribute('xmlns:atom'), "http://www.w3.org/2005/Atom";)
+        self.assertEqual(doc.getElementsByTagName('title')[1].toxml(),
             """<title>po (gedit/User Interface) - gedit (gnome-2-24) - Reserve for translation\n</title>""")
-        self.assertContains(response,
+        self.assertEqual(doc.getElementsByTagName('guid')[0].toxml(),
             """<guid>http://example.com/vertimus/gedit/gnome-2-24/po/fr#%d</guid>""" % action.id)
 
         response = self.client.get(reverse('team_feed', args=[self.l.team.name]))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]