Refactored testing code
- Legacy-Id: 1724
This commit is contained in:
parent
4363336917
commit
4077ea88ab
6
ietf/announcements/tests.py
Normal file
6
ietf/announcements/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class AnnouncementsUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
5
ietf/idindex/tests.py
Normal file
5
ietf/idindex/tests.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IdIndexUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/idrfc/tests.py
Normal file
6
ietf/idrfc/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IdRfcUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
|
@ -3,6 +3,7 @@
|
|||
#import doctest
|
||||
#import templatetags.ietf_filters
|
||||
from django.test import TestCase
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IDTrackerTest(TestCase):
|
||||
def testDoctest(self):
|
||||
|
@ -11,4 +12,8 @@ class IDTrackerTest(TestCase):
|
|||
# bit of extra effort to have doctests run.
|
||||
|
||||
#doctest.testmod(templatetags.ietf_filters)
|
||||
pass
|
||||
pass
|
||||
|
||||
class IdTrackerUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
||||
|
|
6
ietf/iesg/tests.py
Normal file
6
ietf/iesg/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IesgUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/ietfauth/tests.py
Normal file
6
ietf/ietfauth/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IetfAuthUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/ipr/tests.py
Normal file
6
ietf/ipr/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class IprUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/liaisons/tests.py
Normal file
6
ietf/liaisons/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class LiaisonsUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
|
@ -34,6 +34,11 @@ import unittest
|
|||
import re
|
||||
from django.test.client import Client
|
||||
from ietf.utils.test_utils import RealDatabaseTest
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class MailingListsUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
||||
|
||||
class NonWgWizardAddTest(unittest.TestCase):
|
||||
def testAddStep1(self):
|
||||
|
|
6
ietf/meeting/tests.py
Normal file
6
ietf/meeting/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class MeetingUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/my/tests.py
Normal file
6
ietf/my/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class MyUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
6
ietf/redirects/tests.py
Normal file
6
ietf/redirects/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class RedirectsUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
|
@ -158,9 +158,7 @@ INTERNAL_IPS = (
|
|||
SERVER_MODE = 'development'
|
||||
|
||||
# The name of the method to use to invoke the test suite
|
||||
TEST_RUNNER = 'ietf.tests.run_tests'
|
||||
|
||||
TEST_REFERENCE_URL_PREFIX = os.environ.get("IETFDB_REF_PREFIX","") or 'https://datatracker.ietf.org/'
|
||||
TEST_RUNNER = 'ietf.utils.test_runner.run_tests'
|
||||
|
||||
# Override this in settings_local.py if needed
|
||||
# *_PATH variables ends with a slash/ .
|
||||
|
|
565
ietf/tests.py
565
ietf/tests.py
|
@ -1,565 +0,0 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import urllib2 as urllib
|
||||
import httplib
|
||||
from urlparse import urljoin
|
||||
from datetime import datetime
|
||||
|
||||
from ietf.utils.soup2text import TextSoup
|
||||
from difflib import unified_diff
|
||||
|
||||
import django.test.simple
|
||||
from django.test import TestCase
|
||||
from django.test.client import Client
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
from django.core import management
|
||||
import ietf.urls
|
||||
from ietf.utils import log
|
||||
from ietf.utils.test_utils import RealDatabaseTest
|
||||
|
||||
# This is needed so that "manage.py test ietf.UrlTestCase" works
|
||||
if django.VERSION[0] > 0:
|
||||
# "ietf" is not a real application (because it doesn't have models.py),
|
||||
# but let's make it look like one
|
||||
settings.INSTALLED_APPS = settings.INSTALLED_APPS + ['ietf']
|
||||
import types
|
||||
import ietf
|
||||
ietf.models = types.ModuleType('ietf.models')
|
||||
# Fixture loading code uses the directory of this file, not the actual file
|
||||
ietf.models.__file__ = ietf.settings.__file__
|
||||
sys.modules['ietf.models'] = ietf.models
|
||||
|
||||
|
||||
startup_database = settings.DATABASE_NAME # The startup database name, before changing to test_...
|
||||
|
||||
# Test that test/r5106.patch has been applied. This is not written
|
||||
# as normal test case, because it needs to be run before Django's
|
||||
# test framework takes over. This test applies only to Django 0.96,
|
||||
# and can be removed once we transition to 1.x.
|
||||
def test_django_foreignkey_patch():
|
||||
if django.VERSION[0] > 0:
|
||||
return
|
||||
try:
|
||||
t = django.core.management._get_sql_model_create(ietf.idtracker.models.GoalMilestone)
|
||||
except KeyError, f:
|
||||
if str(f.args) == "('ForeignKey',)":
|
||||
raise Exception("Django 0.96 patch in test/r5106.patch not installed?")
|
||||
else:
|
||||
raise
|
||||
|
||||
def run_tests(module_list, verbosity=0, extra_tests=[], interactive=True):
|
||||
test_django_foreignkey_patch()
|
||||
if django.VERSION[0] == 0:
|
||||
module_list.append(ietf.urls)
|
||||
else:
|
||||
if len(module_list) == 0:
|
||||
module_list = ('ietf',)
|
||||
# If we append 'ietf.tests', we get it twice, first as itself, then
|
||||
# during the search for a 'tests' module ...
|
||||
return django.test.simple.run_tests(module_list, 0, extra_tests)
|
||||
|
||||
def normalize_html(html, fill):
|
||||
# Line ending normalization
|
||||
html = html.replace("\r\n", "\n").replace("\r", "\n")
|
||||
# remove comments
|
||||
html = re.sub("(?s)<!--.*?-->", "", html)
|
||||
# attempt to close <li>s (avoid too deep recursion later)
|
||||
if html.count("<li>") > 5*html.count("</li>"):
|
||||
html = html.replace("<li>", "</li><li>")
|
||||
if not fill:
|
||||
html = re.sub("<br ?/?>", "<br/><br/>", html)
|
||||
html = re.sub(r"(?i)(RFC) (\d+)", r"\1\2", html) # ignore "RFC 1234" vs. "RFC1234" diffs
|
||||
html = re.sub(r"\bID\b", r"I-D", html) # idnore " ID " vs. " I-D " diffs
|
||||
# some preprocessing to handle common pathological cases
|
||||
html = re.sub("<br */?>[ \t\n]*(<br */?>)+", "<p/>", html)
|
||||
html = re.sub("<br */?>([^\n])", r"<br />\n\1", html)
|
||||
return html
|
||||
|
||||
def reduce_text(html, pre=False, fill=True):
|
||||
html = normalize_html(html, fill)
|
||||
page = TextSoup(html)
|
||||
text = page.as_text(encoding='latin-1', pre=pre, fill=fill).strip()
|
||||
text = text.replace(" : ", ": ").replace(" :", ": ")
|
||||
text = text.replace('."', '".')
|
||||
text = text.replace(',"', '",')
|
||||
return text, page
|
||||
|
||||
def update_reachability(url, code, page):
|
||||
if code in ["301", "302", "303", "307"]:
|
||||
try:
|
||||
file = urllib.urlopen(url)
|
||||
html = file.read()
|
||||
file.close()
|
||||
code = 200
|
||||
page = TextSoup(html)
|
||||
except urllib.URLError, e:
|
||||
note(" Error retrieving %s: %s" % (url, e))
|
||||
code = e.code
|
||||
page = None
|
||||
module.reachability[url] = (code, "Test")
|
||||
links = ( [ urljoin(url, a["href"]) for a in page.findAll("a") if a.has_key("href")]
|
||||
+ [ urljoin(url, img["src"]) for img in page.findAll("img") if a.has_key("src")] )
|
||||
for link in links:
|
||||
link = link.split("#")[0] # don't include fragment identifier
|
||||
if not link in module.reachability:
|
||||
module.reachability[link] = (None, url)
|
||||
|
||||
def lines(text, pre=False):
|
||||
if pre:
|
||||
text = text.split("\n")
|
||||
else:
|
||||
text = [ line.strip() for line in text.split("\n") if line.strip()]
|
||||
return text
|
||||
|
||||
def sorted(lst):
|
||||
lst.sort()
|
||||
return lst
|
||||
|
||||
def get_patterns(module):
|
||||
all = []
|
||||
try:
|
||||
patterns = module.urlpatterns
|
||||
except AttributeError:
|
||||
patterns = []
|
||||
for item in patterns:
|
||||
try:
|
||||
subpatterns = get_patterns(item.urlconf_module)
|
||||
except:
|
||||
subpatterns = [""]
|
||||
for sub in subpatterns:
|
||||
if not sub:
|
||||
all.append(item.regex.pattern)
|
||||
elif sub.startswith("^"):
|
||||
all.append(item.regex.pattern + sub[1:])
|
||||
else:
|
||||
all.append(item.regex.pattern + ".*" + sub)
|
||||
return all
|
||||
|
||||
def split_url(url):
|
||||
if "?" in url:
|
||||
url, args = url.split("?", 1)
|
||||
args = dict([ arg.split("=", 1) for arg in args.split("&") if "=" in arg ])
|
||||
else:
|
||||
args = {}
|
||||
return url, args
|
||||
|
||||
def read_testurls(filename):
|
||||
tuples = []
|
||||
file = open(filename)
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
line = line.split("#", 1)[0]
|
||||
urlspec = line.split()
|
||||
if len(urlspec) == 2:
|
||||
codes, testurl = urlspec
|
||||
goodurl = None
|
||||
elif len(urlspec) == 3:
|
||||
codes, testurl, goodurl = urlspec
|
||||
# strip protocol and host -- we're making that configurable
|
||||
goodurl = re.sub("^https?://[a-z0-9.]+", "", goodurl)
|
||||
if not goodurl.startswith("/"):
|
||||
goodurl = "/" + goodurl
|
||||
else:
|
||||
raise ValueError("Expected 'HTTP_CODE TESTURL [GOODURL]' in %s line, found '%s'." % (filename, line))
|
||||
|
||||
|
||||
codes = dict([ (item, "") for item in codes.split(",") if not":" in item] +
|
||||
[ (item.split(":")[:2]) for item in codes.split(",") if ":" in item] )
|
||||
tuples += [ (codes, testurl, goodurl) ]
|
||||
file.close()
|
||||
return tuples
|
||||
|
||||
def get_testurls():
|
||||
testtuples = []
|
||||
for root, dirs, files in os.walk(settings.BASE_DIR):
|
||||
if "testurl.list" in files:
|
||||
testtuples += read_testurls(root+"/testurl.list")
|
||||
if "testurls.list" in files:
|
||||
testtuples += read_testurls(root+"/testurls.list")
|
||||
return testtuples
|
||||
|
||||
def filetext(filename):
|
||||
file = open(filename)
|
||||
chunk = file.read()
|
||||
file.close()
|
||||
return chunk
|
||||
|
||||
|
||||
prev_note_time = datetime.utcnow()
|
||||
def note(string):
|
||||
global prev_note_time
|
||||
"""Like a print function, but adds a leading timestamp line"""
|
||||
now = datetime.utcnow()
|
||||
print string
|
||||
print now.strftime(" %Y-%m-%d_%H:%M"), "+%3.1f" % (now-prev_note_time).seconds
|
||||
prev_note_time = datetime.utcnow()
|
||||
|
||||
def module_setup(module):
|
||||
# get selected prefixes, if any
|
||||
module.prefixes = os.environ.get("URLPREFIX", "").split()
|
||||
|
||||
# find test urls
|
||||
module.testtuples = []
|
||||
module.testurls = []
|
||||
module.diffchunks = {}
|
||||
module.ignores = {}
|
||||
module.testtuples = get_testurls()
|
||||
module.testurls = [ tuple[1] for tuple in module.testtuples ]
|
||||
module.reachability = {}
|
||||
|
||||
# find diff chunks
|
||||
testdir = os.path.abspath(settings.BASE_DIR+"/../test/diff/")
|
||||
for item in os.listdir(testdir):
|
||||
path = testdir + "/" + item
|
||||
if item.startswith("generic-") and os.path.isfile(path):
|
||||
chunk = filetext(path).rstrip()
|
||||
chunk = re.sub(r"([\[\]().|+*?])", r"\\\1", chunk)
|
||||
# @@ -27,0 \+23,1 @@
|
||||
chunk = re.sub(r"(?m)^@@ -\d+,(\d+) \\\+\d+,(\d+) @@$", r"@@ -\d+,\1 \+\d+,\2 @@", chunk)
|
||||
module.diffchunks[item] = chunk
|
||||
|
||||
# find ignore chunks
|
||||
for root, dirs, files in os.walk(settings.BASE_DIR+"/../test/ignore/"):
|
||||
# This only expects one directory level below test/ignore/:
|
||||
for file in files:
|
||||
path = root + "/" + file
|
||||
dir = root.split("/")[-1]
|
||||
chunk = filetext(path).strip()
|
||||
if not dir in module.ignores:
|
||||
module.ignores[dir] = []
|
||||
module.ignores[dir].append(chunk)
|
||||
|
||||
# extract application urls:
|
||||
module.patterns = get_patterns(ietf.urls)
|
||||
|
||||
# apply prefix filters
|
||||
if module.prefixes:
|
||||
module.patterns = [ pattern for pattern in module.patterns for prefix in module.prefixes if re.match(prefix, pattern) ]
|
||||
module.testtuples = [ tuple for tuple in module.testtuples for prefix in module.prefixes if re.match(prefix, tuple[1][1:]) ]
|
||||
|
||||
|
||||
# Use the default database for the url tests, instead of the test database
|
||||
module.testdb = settings.DATABASE_NAME
|
||||
connection.close()
|
||||
settings.DATABASE_NAME = startup_database
|
||||
# Install updated fixtures:
|
||||
# Also has the side effect of creating the database connection.
|
||||
if django.VERSION[0] == 0:
|
||||
management.syncdb(verbosity=1, interactive=False)
|
||||
else:
|
||||
management.call_command('syncdb', verbosity=1,noinput=True)
|
||||
connection.close()
|
||||
settings.DATABASE_NAME = module.testdb
|
||||
connection.cursor()
|
||||
|
||||
class UrlTestCase(TestCase,RealDatabaseTest):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
TestCase.__init__(self, *args, **kwargs)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Setup and tear-down
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.setUpRealDatabase()
|
||||
|
||||
def tearDown(self):
|
||||
self.tearDownRealDatabase()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Test methods.
|
||||
#
|
||||
# These are listed in alphabetic order, which is the order in which they will
|
||||
# be executed.
|
||||
#
|
||||
|
||||
def testCoverage(self):
|
||||
covered = []
|
||||
for codes, testurl, goodurl in module.testtuples:
|
||||
for pattern in module.patterns:
|
||||
if re.match(pattern, testurl[1:]):
|
||||
covered.append(pattern)
|
||||
# We should have at least one test case for each url pattern declared
|
||||
# in our Django application:
|
||||
#self.assertEqual(set(patterns), set(covered), "Not all the
|
||||
#application URLs has test cases. The missing are: %s" % (list(set(patterns) - set(covered))))
|
||||
if not set(module.patterns) == set(covered):
|
||||
missing = list(set(module.patterns) - set(covered))
|
||||
print "Not all the application URLs has test cases, there are %d missing." % (len(missing))
|
||||
print "The ones missing are: "
|
||||
for pattern in missing:
|
||||
if not pattern[1:].split("/")[0] in [ "admin", "accounts" ]:
|
||||
print "NoTest", pattern
|
||||
print ""
|
||||
else:
|
||||
print "All the application URL patterns seem to have test cases."
|
||||
#print "Not all the application URLs has test cases."
|
||||
|
||||
def testRedirectsList(self):
|
||||
note("\nTesting specified Redirects:")
|
||||
self.doRedirectsTest(module.testtuples)
|
||||
|
||||
def testUrlsFallback(self):
|
||||
note("\nFallback: Test access to URLs which don't have an explicit test entry:")
|
||||
lst = []
|
||||
for pattern in module.patterns:
|
||||
if pattern.startswith("^") and pattern.endswith("$"):
|
||||
url = "/"+pattern[1:-1]
|
||||
# if there is no variable parts in the url, test it
|
||||
if re.search("^[-a-z0-9./_]*$", url) and not url in module.testurls and not url.startswith("/admin/"):
|
||||
lst.append((["200"], url, None))
|
||||
else:
|
||||
#print "No fallback test for %s" % (url)
|
||||
pass
|
||||
else:
|
||||
lst.append((["Skip"], pattern, None))
|
||||
self.doUrlsTest(lst)
|
||||
|
||||
def testUrlsList(self):
|
||||
note("\nTesting specified URLs:")
|
||||
self.doUrlsTest(module.testtuples)
|
||||
|
||||
# Disable this test by not having it start with "test"
|
||||
def xTestUrlsReachability(self):
|
||||
# This test should be sorted after the other tests which retrieve URLs
|
||||
note("\nTesting URL reachability of %s URLs:" % len(module.reachability) )
|
||||
for url in module.reachability:
|
||||
self.client = Client()
|
||||
if url:
|
||||
code, source = module.reachability[url]
|
||||
if not code:
|
||||
print " %s" % ( url.strip() )
|
||||
if url.startswith("/"):
|
||||
baseurl, args = split_url(url)
|
||||
try:
|
||||
code = str(self.client.get(baseurl, args).status_code)
|
||||
except AssertionError:
|
||||
note("Exception for URL '%s'" % url)
|
||||
traceback.print_exc()
|
||||
code = "Exc"
|
||||
elif url.startswith("mailto:"):
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
file = urllib.urlopen(url)
|
||||
file.close()
|
||||
code = "200"
|
||||
except urllib.HTTPError, e:
|
||||
code = str(e.code)
|
||||
except urllib.URLError, e:
|
||||
note("Exception for URL '%s'" % url)
|
||||
traceback.print_exc()
|
||||
code = "Exc"
|
||||
except httplib.InvalidURL, e:
|
||||
note("Exception for URL '%s'" % url)
|
||||
traceback.print_exc()
|
||||
code = "Exc"
|
||||
else:
|
||||
code = "000"
|
||||
if not code in ["200"]:
|
||||
note("Reach %3s <%s> (from %s)\n" % (code, url, source))
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Worker methods
|
||||
|
||||
def doRedirectsTest(self, lst):
|
||||
response_count = {}
|
||||
for codes, url, master in lst:
|
||||
self.client = Client()
|
||||
if "skipredir" in codes or "Skipredir" in codes or "skipredirect" in codes:
|
||||
print "Skipping %s" % (url)
|
||||
elif url and master:
|
||||
testurl = master.replace('https://datatracker.ietf.org','')
|
||||
baseurl, args = split_url(testurl)
|
||||
try:
|
||||
response = self.client.get(baseurl, args)
|
||||
code = str(response.status_code)
|
||||
if code == "301":
|
||||
if response['Location'] == url:
|
||||
note("OK %s %s -> %s" % (code, testurl, url))
|
||||
res = ("OK", code)
|
||||
else:
|
||||
print "Miss %3s %s ->" % (code, testurl)
|
||||
print " %s" % (response['Location'])
|
||||
note( " (wanted %s)" % (url))
|
||||
print ""
|
||||
res = None
|
||||
#res = ("Fail", "wrong-reponse")
|
||||
else:
|
||||
note("Fail %s %s" % (code, testurl))
|
||||
res = ("Fail", code)
|
||||
except:
|
||||
res = ("Fail", "Exc")
|
||||
note("Exception for URL '%s'" % testurl)
|
||||
traceback.print_exc()
|
||||
if res:
|
||||
if not res in response_count:
|
||||
response_count[res] = 0
|
||||
response_count[res] += 1
|
||||
if response_count:
|
||||
print ""
|
||||
note("Response count:")
|
||||
for res in response_count:
|
||||
ind, code = res
|
||||
print " %-4s %s: %s " % (ind, code, response_count[res])
|
||||
for res in response_count:
|
||||
ind, code = res
|
||||
self.assertEqual(ind, "OK", "Found %s cases of result code: %s" % (response_count[res], code))
|
||||
if response_count:
|
||||
print ""
|
||||
|
||||
def doUrlsTest(self, lst):
|
||||
response_count = {}
|
||||
for codes, url, master in lst:
|
||||
self.client = Client()
|
||||
if "skip" in codes or "Skip" in codes:
|
||||
print "Skipping %s" % (url)
|
||||
elif url:
|
||||
baseurl, args = split_url(url)
|
||||
#print "Trying codes, url: (%s, '%s')" % (codes, url)
|
||||
try:
|
||||
response = self.client.get(baseurl, args)
|
||||
code = str(response.status_code)
|
||||
if code in codes:
|
||||
note("OK %s %s" % (code, url))
|
||||
res = ("OK", code)
|
||||
else:
|
||||
note("Fail %s %s" % (code, url))
|
||||
res = ("Fail", code)
|
||||
except:
|
||||
res = ("Fail", "Exc")
|
||||
note("Exception for URL '%s'" % url)
|
||||
traceback.print_exc()
|
||||
if master and not "skipdiff" in codes:
|
||||
hostprefix = settings.TEST_REFERENCE_URL_PREFIX
|
||||
if hostprefix.endswith("/"):
|
||||
hostprefix = hostprefix[:-1]
|
||||
master = hostprefix + master
|
||||
goodhtml = None
|
||||
try:
|
||||
#print "Fetching", master, "...",
|
||||
mfile = urllib.urlopen(master)
|
||||
goodhtml = mfile.read()
|
||||
mfile.close()
|
||||
note(" 200 %s" % (master))
|
||||
except urllib.URLError, e:
|
||||
note(" Error retrieving %s: %s" % (master, e))
|
||||
except urllib.HTTPError, e:
|
||||
note(" Error retrieving %s: %s" % (master, e))
|
||||
try:
|
||||
if goodhtml and response.content:
|
||||
testhtml = response.content
|
||||
# Always ignore some stuff
|
||||
for regex in module.ignores["always"]:
|
||||
testhtml = re.sub(regex, "", testhtml)
|
||||
goodhtml = re.sub(regex, "", goodhtml)
|
||||
if "sort" in codes:
|
||||
testtext, testpage = reduce_text(testhtml, fill=False)
|
||||
goodtext, goodpage = reduce_text(goodhtml, fill=False)
|
||||
else:
|
||||
testtext, testpage = reduce_text(response.content)
|
||||
goodtext, goodpage = reduce_text(goodhtml)
|
||||
update_reachability(url, code, testpage)
|
||||
# Always ignore some stuff again
|
||||
for regex in module.ignores["always"]:
|
||||
testtext = re.sub(regex, "", testtext)
|
||||
goodtext = re.sub(regex, "", goodtext)
|
||||
if "ignore" in codes:
|
||||
ignores = codes["ignore"].split("/")
|
||||
for ignore in ignores:
|
||||
for regex in module.ignores[ignore]:
|
||||
testtext = re.sub(regex, "", testtext)
|
||||
goodtext = re.sub(regex, "", goodtext)
|
||||
#log("Checking text: %s" % testtext[:96])
|
||||
testtext = lines(testtext.strip())
|
||||
goodtext = lines(goodtext.strip())
|
||||
if "sort" in codes:
|
||||
testtext = sorted(testtext)
|
||||
while testtext and not testtext[0]:
|
||||
del testtext[0]
|
||||
while testtext and not testtext[-1]:
|
||||
del testtext[-1]
|
||||
goodtext = sorted(goodtext)
|
||||
while goodtext and not goodtext[0]:
|
||||
del goodtext[0]
|
||||
while goodtext and not goodtext[-1]:
|
||||
del goodtext[-1]
|
||||
if testtext == goodtext:
|
||||
note("OK cmp %s" % (url))
|
||||
else:
|
||||
contextlines = 0
|
||||
difflist = list(unified_diff(goodtext, testtext, master, url, "", "", contextlines, lineterm=""))
|
||||
diff = "\n".join(difflist[2:])
|
||||
log("Checking diff: %s" % diff[:96])
|
||||
keys = module.diffchunks.keys()
|
||||
keys.sort
|
||||
for key in keys:
|
||||
chunk = module.diffchunks[key]
|
||||
if chunk:
|
||||
if not re.search(chunk, diff):
|
||||
log("No match: %s" % chunk[:96])
|
||||
while re.search(chunk, diff):
|
||||
log("Found chunk: %s" % chunk[:96])
|
||||
diff = re.sub(chunk, "", diff)
|
||||
if len(diff.strip().splitlines()) == 2:
|
||||
# only the initial 2 lines of the diff remains --
|
||||
# discard them too
|
||||
diff = ""
|
||||
if diff:
|
||||
dfile = "%s/../test/diff/%s" % (settings.BASE_DIR, re.sub("[/?&=]", "_", url) )
|
||||
if os.path.exists(dfile):
|
||||
dfile = open(dfile)
|
||||
#print "Reading OK diff file:", dfile.name
|
||||
okdiff = dfile.read()
|
||||
dfile.close()
|
||||
else:
|
||||
okdiff = ""
|
||||
if diff.strip() == okdiff.strip():
|
||||
note("OK cmp %s" % (url))
|
||||
else:
|
||||
if okdiff:
|
||||
note("Failed diff: %s" % (url))
|
||||
else:
|
||||
note("Diff: %s" % (url))
|
||||
print "\n".join(diff.split("\n")[:100])
|
||||
if len(diff.split("\n")) > 100:
|
||||
print "... (skipping %s lines of diff)" % (len(difflist)-100)
|
||||
else:
|
||||
note("OK cmp %s" % (url))
|
||||
|
||||
except:
|
||||
note("Exception occurred for url %s" % (url))
|
||||
traceback.print_exc()
|
||||
#raise
|
||||
|
||||
if not res in response_count:
|
||||
response_count[res] = 0
|
||||
response_count[res] += 1
|
||||
else:
|
||||
pass
|
||||
if response_count:
|
||||
print ""
|
||||
note("Response count:")
|
||||
for res in response_count:
|
||||
ind, code = res
|
||||
print " %-4s %s: %s " % (ind, code, response_count[res])
|
||||
for res in response_count:
|
||||
ind, code = res
|
||||
self.assertEqual(ind, "OK", "Found %s cases of result code: %s" % (response_count[res], code))
|
||||
if response_count:
|
||||
print ""
|
||||
|
||||
|
||||
|
||||
class Module:
|
||||
pass
|
||||
module = Module()
|
||||
module_setup(module)
|
113
ietf/utils/test_runner.py
Normal file
113
ietf/utils/test_runner.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
# Portion Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# * Neither the name of the Nokia Corporation and/or its
|
||||
# subsidiary(-ies) nor the names of its contributors may be used
|
||||
# to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from django.conf import settings
|
||||
import django
|
||||
|
||||
test_database_name = None
|
||||
old_destroy = None
|
||||
old_create = None
|
||||
|
||||
def safe_create_0(verbosity, *args, **kwargs):
|
||||
global test_database_name, old_create
|
||||
print "Creating test database..."
|
||||
x = old_create(0, *args, **kwargs)
|
||||
print "Saving test database name "+settings.DATABASE_NAME+"..."
|
||||
test_database_name = settings.DATABASE_NAME
|
||||
return x
|
||||
|
||||
def safe_create_1(self, verbosity, *args, **kwargs):
|
||||
global test_database_name, old_create
|
||||
print "Creating test database..."
|
||||
x = old_create(self, 0, *args, **kwargs)
|
||||
print "Saving test database name "+settings.DATABASE_NAME+"..."
|
||||
test_database_name = settings.DATABASE_NAME
|
||||
return x
|
||||
|
||||
def safe_destroy_0_1(*args, **kwargs):
|
||||
global test_database_name, old_destroy
|
||||
print "Checking that it's safe to destroy test database..."
|
||||
if settings.DATABASE_NAME != test_database_name:
|
||||
print "NOT SAFE; Changing settings.DATABASE_NAME from "+settings.DATABASE_NAME+" to "+test_database_name
|
||||
settings.DATABASE_NAME = test_name
|
||||
return old_destroy(*args, **kwargs)
|
||||
|
||||
# Test that test/r5106.patch has been applied. This is not written
|
||||
# as normal test case, because it needs to be run before Django's
|
||||
# test framework takes over. This test applies only to Django 0.96,
|
||||
# and can be removed once we transition to 1.x.
|
||||
def test_django_foreignkey_patch():
|
||||
print "Testing Django 0.96 ForeignKey patch..."
|
||||
try:
|
||||
import ietf
|
||||
t = django.core.management._get_sql_model_create(ietf.idtracker.models.GoalMilestone)
|
||||
except KeyError, f:
|
||||
if str(f.args) == "('ForeignKey',)":
|
||||
raise Exception("Django 0.96 patch in test/r5106.patch not installed?")
|
||||
else:
|
||||
raise
|
||||
|
||||
def run_tests_0(*args, **kwargs):
|
||||
global old_destroy, old_create, test_database_name
|
||||
import django.test.utils
|
||||
m = sys.modules['django.test.utils']
|
||||
old_create = m.create_test_db
|
||||
m.create_test_db = safe_create_0
|
||||
old_destroy = m.destroy_test_db
|
||||
m.destroy_test_db = safe_destroy_0_1
|
||||
from django.test.simple import run_tests
|
||||
run_tests(*args, **kwargs)
|
||||
|
||||
|
||||
def run_tests_1(test_labels, *args, **kwargs):
|
||||
global old_destroy, old_create, test_database_name
|
||||
from django.db import connection
|
||||
old_create = connection.creation.__class__.create_test_db
|
||||
connection.creation.__class__.create_test_db = safe_create_1
|
||||
old_destroy = connection.creation.__class__.destroy_test_db
|
||||
connection.creation.__class__.destroy_test_db = safe_destroy_0_1
|
||||
from django.test.simple import run_tests
|
||||
if not test_labels:
|
||||
test_labels = [x.split(".")[-1] for x in settings.INSTALLED_APPS if x.startswith("ietf")]
|
||||
run_tests(test_labels, *args, **kwargs)
|
||||
|
||||
def run_tests(*args, **kwargs):
|
||||
if django.VERSION[0] == 0:
|
||||
test_django_foreignkey_patch()
|
||||
run_tests_0(*args, **kwargs)
|
||||
else:
|
||||
run_tests_1(*args, **kwargs)
|
||||
|
|
@ -32,9 +32,20 @@
|
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
from django.db import connection
|
||||
from django.test import TestCase
|
||||
from django.test.client import Client
|
||||
import ietf
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
import urllib2 as urllib
|
||||
from difflib import unified_diff
|
||||
|
||||
|
||||
import traceback
|
||||
|
||||
class RealDatabaseTest:
|
||||
def setUpRealDatabase(self):
|
||||
|
@ -59,5 +70,124 @@ class RealDatabaseTest:
|
|||
if django.VERSION[0] == 0:
|
||||
django.conf.settings.DATABASE_NAME = name
|
||||
else:
|
||||
django.conf.settings.DATABASE_NAME = name
|
||||
connection.settings_dict['DATABASE_NAME'] = name
|
||||
connection.cursor()
|
||||
|
||||
def read_testurls(filename):
|
||||
tuples = []
|
||||
file = open(filename)
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
line = line.split("#", 1)[0]
|
||||
urlspec = line.split()
|
||||
if len(urlspec) == 2:
|
||||
codes, testurl = urlspec
|
||||
goodurl = None
|
||||
elif len(urlspec) == 3:
|
||||
codes, testurl, goodurl = urlspec
|
||||
# strip protocol and host -- we're making that configurable
|
||||
goodurl = re.sub("^https?://[a-z0-9.]+", "", goodurl)
|
||||
if not goodurl.startswith("/"):
|
||||
goodurl = "/" + goodurl
|
||||
else:
|
||||
raise ValueError("Expected 'HTTP_CODE TESTURL [GOODURL]' in %s line, found '%s'." % (filename, line))
|
||||
|
||||
|
||||
codes = dict([ (item, "") for item in codes.split(",") if not":" in item] +
|
||||
[ (item.split(":")[:2]) for item in codes.split(",") if ":" in item] )
|
||||
tuples += [ (codes, testurl, goodurl) ]
|
||||
file.close()
|
||||
return tuples
|
||||
|
||||
def split_url(url):
|
||||
if "?" in url:
|
||||
url, args = url.split("?", 1)
|
||||
args = dict([ arg.split("=", 1) for arg in args.split("&") if "=" in arg ])
|
||||
else:
|
||||
args = {}
|
||||
return url, args
|
||||
|
||||
class SimpleUrlTestCase(TestCase,RealDatabaseTest):
|
||||
|
||||
def setUp(self):
|
||||
self.setUpRealDatabase()
|
||||
self.client = Client()
|
||||
self.ref_prefix = os.environ.get("IETFDB_REF_PREFIX", "")
|
||||
if self.ref_prefix.endswith("/"):
|
||||
self.ref_prefix = self.ref_prefix[:-1]
|
||||
|
||||
def tearDown(self):
|
||||
self.tearDownRealDatabase()
|
||||
|
||||
def doTestUrls(self, test_filename):
|
||||
filename = os.path.dirname(os.path.abspath(test_filename))+"/testurl.list"
|
||||
print "Reading "+filename
|
||||
tuples = read_testurls(filename)
|
||||
failures = 0
|
||||
for tuple in tuples:
|
||||
try:
|
||||
self.doTestUrl(tuple)
|
||||
except:
|
||||
failures = failures + 1
|
||||
self.assertEqual(failures, 0, "%d URLs failed" % failures)
|
||||
|
||||
def doTestUrl(self, tuple):
|
||||
(codes, url, master) = tuple
|
||||
baseurl, args = split_url(url)
|
||||
failed = False
|
||||
#enable this to see query counts
|
||||
#settings.DEBUG = True
|
||||
try:
|
||||
now = datetime.utcnow()
|
||||
response = self.client.get(baseurl, args)
|
||||
elapsed_dt = datetime.utcnow()-now
|
||||
elapsed = elapsed_dt.seconds + elapsed_dt.microseconds/1e6
|
||||
code = str(response.status_code)
|
||||
queries = len(connection.queries)
|
||||
if code in codes:
|
||||
print "OK %s %s" % (code, url)
|
||||
else:
|
||||
print "Fail %s %s" % (code, url)
|
||||
failed = True
|
||||
if queries > 0:
|
||||
print " (%.1f s, %d kB, %d queries)" % (elapsed, len(response.content)/1000, queries)
|
||||
else:
|
||||
print " (%.1f s, %d kB)" % (elapsed, len(response.content)/1000)
|
||||
if code in codes and code == "200":
|
||||
self.doDiff(tuple, response)
|
||||
except:
|
||||
failed = True
|
||||
print "Exception for URL '%s'" % url
|
||||
traceback.print_exc()
|
||||
self.assertEqual(failed, False)
|
||||
|
||||
def doDiff(self, tuple, response):
|
||||
if not self.ref_prefix:
|
||||
return
|
||||
(codes, url, master) = tuple
|
||||
if "skipdiff" in codes:
|
||||
return
|
||||
refurl = self.ref_prefix+url
|
||||
print " Fetching "+refurl
|
||||
refhtml = None
|
||||
try:
|
||||
mfile = urllib.urlopen(refurl)
|
||||
refhtml = mfile.read()
|
||||
mfile.close()
|
||||
except e:
|
||||
print " Error retrieving %s: %s" % (refurl, e)
|
||||
testhtml = response.content
|
||||
|
||||
list0 = refhtml.split("\n")
|
||||
list1 = testhtml.split("\n")
|
||||
diff = "\n".join(unified_diff(list0, list1, url, refurl, "", "", 0, lineterm=""))
|
||||
if diff:
|
||||
print " Differences found:"
|
||||
print diff
|
||||
else:
|
||||
print " No differences found"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#200 /review/
|
||||
#200 /review/0/
|
||||
#200 /review/top/17/
|
||||
#200 /review/all/
|
6
ietf/wginfo/tests.py
Normal file
6
ietf/wginfo/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase
|
||||
|
||||
class WgInfoUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
self.doTestUrls(__file__)
|
Loading…
Reference in a new issue