Add support for testing Django-dependent modules

- Add django-test-settings.py for Django settings
- Add runtests.py for setting up Django test environment
- Add Django test setup support to setup.py and coverage.py
- Add new test module test_kvstore.py
- Enable existing Django-dependent tests in test_context_processors.py and test_menu.py
This commit is contained in:
Bob Girard 2015-05-06 11:09:06 -07:00 committed by Sunil Mohan Adapa
parent 03049b666a
commit 5cadd223d5
8 changed files with 198 additions and 65 deletions

View File

@ -25,7 +25,12 @@ from plinth import cfg
def common(request):
"""Add additional context values to RequestContext for use in templates."""
"""
Add additional context values to RequestContext for use in templates.
Any resources referenced in the return value are expected to have been
initialized or configured externally beforehand.
"""
slash_indices = [match.start() for match in re.finditer('/', request.path)]
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
return {

View File

@ -26,13 +26,11 @@
Support for integration of code test coverage analysis with setuptools.
"""
import coverage
import glob
import setuptools
import shutil
import time
import unittest
from plinth import tests
# Project directories with testable source code
@ -78,22 +76,14 @@ class CoverageCommand(setuptools.Command):
except:
pass
# Initialize a test suite for one or all modules
if self.test_module is None:
test_suite = tests.TEST_SUITE
else:
test = unittest.defaultTestLoader.loadTestsFromNames(
[self.test_module])
test_suite = unittest.TestSuite(test)
# Run the coverage analysis
runner = unittest.TextTestRunner()
import coverage
cov = coverage.coverage(auto_data=True, config_file=True,
source=SOURCE_DIRS, omit=FILES_TO_OMIT)
cov.erase() # Erase existing coverage data file
cov.start()
runner.run(test_suite)
# Invoke the Django test setup and test runner logic
from plinth.tests.runtests import run_tests
run_tests(pattern=self.test_module, return_to_caller=True)
cov.stop()
# Generate an HTML report

View File

@ -0,0 +1,28 @@
# Django settings for test modules.
import os
TEST_DATA_DIR = os.path.dirname(os.path.abspath(__file__))
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(TEST_DATA_DIR, 'plinth.sqlite3'),
}
}
DEFAULT_INDEX_TABLESPACE = ''
INSTALLED_APPS = (
'plinth',
)
# These are included here solely to suppress Django warnings
# during testing setup
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
ROOT_URLCONF = 'plinth.urls'
SECRET_KEY = 'django_tests_secret_key'

44
plinth/tests/runtests.py Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""Module for Django pre-test configuration and setup."""
import os
import sys
import django
from django.conf import settings
from django.test.utils import get_runner
def run_tests(pattern=None, return_to_caller=False):
"""Set up the Django test environment and run the specified tests."""
os.environ['DJANGO_SETTINGS_MODULE'] =\
'plinth.tests.data.django_test_settings'
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
if pattern is None:
pattern = 'plinth.tests'
failures = test_runner.run_tests([pattern])
if failures > 0 or not return_to_caller:
sys.exit(bool(failures))
if __name__ == '__main__':
run_tests()

View File

@ -17,17 +17,19 @@
#
from django.http import HttpRequest
import unittest
from django.test import TestCase
from plinth import cfg
from plinth import context_processors as cp
class ContextProcessorsTestCase(unittest.TestCase):
class ContextProcessorsTestCase(TestCase):
"""Verify behavior of the context_processors module."""
@unittest.skip('requires configuring Django beforehand')
def test_common(self):
"""Verify that the 'common' function returns the correct values."""
"""Verify that the common() function returns the correct values."""
cfg.read() # initialize config settings
request = HttpRequest()
request.path = '/aaa/bbb/ccc/'
response = cp.common(request)
@ -45,9 +47,8 @@ class ContextProcessorsTestCase(unittest.TestCase):
self.assertIsNotNone(urls)
self.assertEqual(['/', '/aaa/', '/aaa/bbb/', '/aaa/bbb/ccc/'], urls)
@unittest.skip('requires configuring Django beforehand')
def test_common_border_conditions(self):
"""Verify that the 'common' functions works for border conditions."""
"""Verify that the common() function works for border conditions."""
request = HttpRequest()
request.path = ''
response = cp.common(request)
@ -60,7 +61,3 @@ class ContextProcessorsTestCase(unittest.TestCase):
request.path = '/aaa/bbb'
response = cp.common(request)
self.assertEqual(['/', '/aaa/'], response['active_menu_urls'])
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,39 @@
#!/usr/bin/python3
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from django.test import TestCase
from plinth import kvstore
class KvstoreTestCase(TestCase):
"""Verify the behavior of the kvstore module."""
def test_get(self):
"""Verify that a set value can be retrieved."""
key = 'name'
expected_value = 'Guido'
kvstore.set(key, expected_value)
actual_value = kvstore.get(key)
self.assertEqual(expected_value, actual_value)
def test_get_default(self):
"""Verify that either a set value or its default can be retrieved."""
expected = 'default'
actual = kvstore.get_default('bad_key', expected)
self.assertEqual(expected, actual)

View File

@ -16,9 +16,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from django.core.urlresolvers import reverse
from django.http import HttpRequest
from django.test import TestCase
import random
import unittest
from plinth.menu import Menu
@ -26,10 +27,35 @@ from plinth.menu import Menu
URL_TEMPLATE = '/a{}/b{}/c{}/'
class MenuTestCase(unittest.TestCase):
"""Verify the behavior of the Plinth Menu class."""
# Test helper methods
# Test methods
def build_menu(size=5):
"""Build a menu with the specified number of items."""
random.seed()
item_data = []
for index in range(1, size + 1):
item_data.append(['Item' + str(index),
'Icon' + str(index),
URL_TEMPLATE.format(index, index, index),
random.randint(0, 1000)])
menu = Menu()
for data in item_data:
menu.add_item(data[0], data[1], data[2], data[3])
return menu
def dump_menu(menu):
"""Dump the specified menu URL hierarchy to the console."""
print()
print('# # # # # # # # # #')
print('Top level URL: %s' % menu.url)
for item in menu.items:
print(' Item URL: %s' % item.url)
print('# # # # # # # # # #')
class MenuTestCase(TestCase):
"""Verify the behavior of the Plinth Menu class."""
def test_menu_creation_without_arguments(self):
"""Verify the Menu state without initialization parameters."""
@ -55,28 +81,40 @@ class MenuTestCase(unittest.TestCase):
self.assertEqual(expected_order, menu.order)
self.assertEqual(0, len(menu.items))
@unittest.skip('requires configuring Django beforehand')
def test_get(self):
"""Verify that a menu item can be correctly retrieved."""
expected_label = 'Label2'
expected_icon = 'Icon2'
expected_url = '/ddd/eee/fff/'
expected_url = 'index'
reversed_url = reverse(expected_url)
expected_order = 2
menu = Menu()
menu.add_item(expected_label, expected_icon, expected_url,
menu.add_item(expected_label, expected_icon, reversed_url,
expected_order)
actual_item = menu.get(expected_url)
self.assertIsNotNone(actual_item)
self.assertEqual(expected_label, actual_item.label)
self.assertEqual(expected_icon, actual_item.icon)
self.assertEqual(expected_url, actual_item.url)
self.assertEqual(reversed_url, actual_item.url)
self.assertEqual(expected_order, actual_item.order)
self.assertEqual(0, len(actual_item.items))
def test_get_with_item_not_found(self):
"""Verify that a KeyError is raised if a menu item is not found."""
expected_label = 'Label3'
expected_icon = 'Icon3'
expected_url = 'index'
expected_order = 3
menu = Menu()
menu.add_item(expected_label, expected_icon, expected_url,
expected_order)
self.assertRaises(KeyError, menu.get, expected_url)
def test_sort_items(self):
"""Verify that menu items are sorted correctly."""
menu = self.build_menu()
menu = build_menu()
# Verify that the order of every item is equal to or greater
# than the order of the item preceding it
@ -84,16 +122,30 @@ class MenuTestCase(unittest.TestCase):
self.assertGreaterEqual(menu.items[index].order,
menu.items[index - 1].order)
@unittest.skip('requires configuring Django beforehand')
def test_add_urlname(self):
"""Verify that a named URL can be added to a menu correctly."""
expected_label = 'Label4'
expected_icon = 'Icon4'
expected_url = 'index'
reversed_url = reverse(expected_url)
expected_order = 4
menu = Menu()
actual_item = menu.add_urlname(expected_label, expected_icon,
expected_url, expected_order)
self.assertIsNotNone(actual_item)
self.assertEqual(expected_label, actual_item.label)
self.assertEqual(expected_icon, actual_item.icon)
self.assertEqual(reversed_url, actual_item.url)
self.assertEqual(expected_order, actual_item.order)
self.assertEqual(0, len(actual_item.items))
def test_add_item(self):
"""Verify that a menu item can be correctly added."""
expected_label = 'Label3'
expected_icon = 'Icon3'
expected_url = '/ggg/hhh/iii/'
expected_order = 3
expected_label = 'Label5'
expected_icon = 'Icon5'
expected_url = '/jjj/kkk/lll/'
expected_order = 5
menu = Menu()
actual_item = menu.add_item(expected_label, expected_icon,
expected_url, expected_order)
@ -105,10 +157,9 @@ class MenuTestCase(unittest.TestCase):
self.assertEqual(expected_order, actual_item.order)
self.assertEqual(0, len(actual_item.items))
@unittest.skip('requires configuring Django beforehand')
def test_active_item(self):
"""Verify that an active menu item can be correctly retrieved."""
menu = self.build_menu()
menu = build_menu()
for index in range(1, 8):
request = HttpRequest()
@ -120,33 +171,12 @@ class MenuTestCase(unittest.TestCase):
else:
self.assertIsNone(item)
@unittest.skip('requires configuring Django beforehand')
def test_active_item_when_inside_subpath(self):
"""Verify that the current URL could be a sub-path of menu item."""
menu = self.build_menu()
"""Verify that the current URL could be a sub-path of a menu item."""
menu = build_menu()
expected_url = URL_TEMPLATE.format(1, 1, 1)
request = HttpRequest()
request.path = expected_url + 'd/e/f/'
item = menu.active_item(request)
self.assertEqual('Item1', item.label)
self.assertEqual(expected_url, item.url)
# Helper methods
def build_menu(self, size=5):
"""Build a menu with the specified number of items."""
random.seed()
item_data = []
for index in range(1, size + 1):
item_data.append(['Item' + str(index),
'Icon' + str(index),
URL_TEMPLATE.format(index, index, index),
random.randint(0, 1000)])
menu = Menu()
for data in item_data:
menu.add_item(data[0], data[1], data[2], data[3])
return menu
if __name__ == '__main__':
unittest.main()

View File

@ -93,7 +93,7 @@ setuptools.setup(
packages=find_packages(include=['plinth', 'plinth.*'],
exclude=['*.templates']),
scripts=['bin/plinth'],
test_suite='plinth.tests.TEST_SUITE',
test_suite='plinth.tests.runtests.run_tests',
license='COPYING',
classifiers=[
'Development Status :: 3 - Alpha',