mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
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:
parent
03049b666a
commit
5cadd223d5
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
28
plinth/tests/data/django_test_settings.py
Normal file
28
plinth/tests/data/django_test_settings.py
Normal 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
44
plinth/tests/runtests.py
Normal 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()
|
||||
@ -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()
|
||||
|
||||
39
plinth/tests/test_kvstore.py
Normal file
39
plinth/tests/test_kvstore.py
Normal 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)
|
||||
@ -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()
|
||||
|
||||
2
setup.py
2
setup.py
@ -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',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user