Commit e394f46e authored by FichteFoll's avatar FichteFoll

Implement tests using unittest completely

For some reason `nosetests` doesn't run the generated tests so I changed the build command for travis
parent c20ea112
language: python language: python
python: python:
- "3.3" - "3.3"
#command to run tests #command to run tests
script: nosetests script: python -m unittest
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Tests for the validity of the channel file """Tests for the validity of the channel and repository files.
You can run this file directly or with `notetests` (or `python -m unittest`) You can run this script directly or with `python -m unittest` from this or the
from the root directory. root directory. For some reason `nosetests` does not pick up the generated tests
even though they are generated at load time.
""" """
import os import os
import json import json
import unittest import unittest
from collections import OrderedDict from collections import OrderedDict
from nose.tools import assert_equal, assert_in, assert_not_in, assert_regexp_matches from functools import wraps
################################################################################
def _open(filepath, *args, **kwargs):
"""Wrapper function that can search one dir above if the desired file
does not exist.
"""
if not os.path.exists(filepath):
filepath = os.path.join("..", filepath)
return open(filepath, *args, **kwargs)
def generator_class(cls):
"""Class decorator for class that use generating methods.
A class that is decorated with this function will be searched for methods
starting with "generate_" (similar to "test_") and then run like a nosetest
generator.
Note: The generator function must be a classmethod!
Generate tests using the following statement:
yield function, (arg1, arg2, arg3) # ...
"""
for name in list(cls.__dict__.keys()):
if not name.startswith("generate_") or not callable(getattr(cls, name)):
continue
# Create new methods for each `yield`
for sub_call in getattr(cls, name)():
method, params = sub_call
@wraps(method)
def wrapper(self, method=method, params=params):
return method(self, *params)
# Do not attempt to print lists/dicts with printed lenght of 1000 or
# more, they are not interesting for us (probably the whole file)
args = []
for v in params:
string = repr(v)
if len(string) > 1000:
args.append('...')
else:
args.append(repr(v))
name = "%s(%s)" % (method.__name__.replace("_test", "test"),
", ".join(args))
setattr(cls, name, wrapper)
# Generator tests can't be part of a class, so for consistency # Remove the generator afterwards, it did its work
# they are all functions delattr(cls, name)
def test_channel(): # print(dir(cls))
with open("channel.json") as fp: return cls
data = json.load(fp)
keys = sorted(data.keys())
assert_equal(keys, ['repositories', 'schema_version'])
assert_equal(data['schema_version'], '2.0')
assert_equal(type(data['repositories']), list)
for repository in data['repositories']: def get_package_name(data):
assert_equal(type(repository), str) """Gets "name" from a package with a workaround when it's not defined.
Use the last part of details url for the package's name otherwise since
packages must one of these two keys anyway.
"""
return data.get('name', data.get('details', '').rsplit('/', 1)[-1])
def test_repository():
with open('repository.json') as f:
data = json.load(f, object_pairs_hook=OrderedDict)
keys = sorted(data.keys()) ################################################################################
assert_equal(keys, ['includes', 'packages', 'schema_version'])
class ChannelTests(unittest.TestCase):
with _open('channel.json') as f:
j = json.load(f)
def test_channel_keys(self):
keys = sorted(self.j.keys())
self.assertEqual(keys, ['repositories', 'schema_version'])
assert_equal(data['schema_version'], '2.0') self.assertEqual(self.j['schema_version'], '2.0')
assert_equal(data['packages'], []) self.assertIsInstance(self.j['repositories'], list)
assert_equal(type(data['includes']), list)
for repo in self.j['repositories']:
self.assertIsInstance(repo, str)
@generator_class
class RepositoryTests(unittest.TestCase):
with _open('repository.json') as f:
j = json.load(f, object_pairs_hook=OrderedDict)
key_types_map = {
'name': str,
'details': str,
'releases': list,
'homepage': str,
'author': str,
'readme': str,
'issues': str,
'donate': str,
'buy': str,
'previous_names': list,
'labels': list
}
for include in data['includes']: def test_repository_keys(self):
assert_equal(type(include), str) keys = sorted(self.j.keys())
self.assertEqual(keys, ['includes', 'packages', 'schema_version'])
self.assertEqual(self.j['schema_version'], '2.0')
self.assertEqual(self.j['packages'], [])
self.assertIsInstance(self.j['includes'], list)
def test_repository_includes(): for include in self.j['includes']:
with open('repository.json') as f: self.assertIsInstance(include, str)
@classmethod
def generate_include_tests(cls):
for include in cls.j['includes']:
try:
with _open(include) as f:
data = json.load(f, object_pairs_hook=OrderedDict) data = json.load(f, object_pairs_hook=OrderedDict)
except Exception as e:
print("adding failure")
yield cls._test_error, ("Error while reading %r" % include, e)
# print("Error while reading %r: %s" % (include, e))
continue
# `include` is for output during tests only
yield cls._test_include_keys, (include, data)
for package in data['packages']:
yield cls._test_package, (include, package)
for include in data['includes']: package_name = get_package_name(data)
yield check_include, include
with open(include) as f:
include_data = json.load(f, object_pairs_hook=OrderedDict)
for package in include_data['packages']:
yield check_package, package
if 'releases' in package: if 'releases' in package:
for release in package['releases']: for release in package['releases']:
yield check_release, package, release yield cls._test_release, (package_name, release)
def _test_include_keys(self, include, data):
def check_include(filename):
with open(filename) as f:
data = json.load(f, object_pairs_hook=OrderedDict)
keys = sorted(data.keys()) keys = sorted(data.keys())
assert_equal(keys, ['packages', 'schema_version']) self.assertEqual(keys, ['packages', 'schema_version'])
assert_equal(data['schema_version'], '2.0') self.assertEqual(data['schema_version'], '2.0')
assert_equal(type(data['packages']), list) self.assertIsInstance(data['packages'], list)
def _test_package(self, include, data):
def check_package(data):
for key in data.keys(): for key in data.keys():
assert_in(key, ['name', 'details', 'releases', 'homepage', 'author', self.assertIn(key, ('name', 'details', 'releases', 'homepage',
'readme', 'issues', 'donate', 'buy', 'previous_names', 'labels']) 'author', 'readme', 'issues', 'donate', 'buy',
assert_equal(type(data[key]), map_key_type(key)) 'previous_names', 'labels'))
if key in ['details', 'homepage', 'readme', 'issues', 'donate', 'buy']: self.assertIsInstance(data[key], self.key_types_map[key])
assert_regexp_matches(data[key], '^https?://')
if key in ('details', 'homepage', 'readme', 'issues', 'donate',
'buy'):
self.assertRegex(data[key], '^https?://')
if 'details' not in data: if 'details' not in data:
assert_in('name', data, 'The key "name" is required if no "details" URL provided') for key in ('name', 'homepage', 'author', 'releases'):
assert_in('homepage', data, 'The key "homepage" is required if no "details" URL provided') self.assertIn(key, data, '%r is required if no "details" URL '
assert_in('author', data, 'The key "author" is required if no "details" URL provided') 'provided' % key)
assert_in('releases', data, 'The key "releases" is required if no "details" URL provided')
def _test_release(self, package_name, data):
# Fail early
self.assertIn('details', data,
'A release must have a "details" key if it is in the '
'main repository. For custom releases, a custom '
'repository.json file must be hosted elsewhere.')
def check_release(package, data):
for key in data.keys(): for key in data.keys():
assert_not_in(key, ['version', 'date', 'url'], 'The version, date and ' + \ # Display this message despite it being tested with the next test
'url keys should not be used in the main repository since a pull ' + \ # anyway
'request would be necessary for every release') self.assertNotIn(key, ('version', 'date', 'url'),
'The version, date and url keys should not be '
'used in the main repository since a pull request '
'would be necessary for every release')
assert_in(key, ['details', 'sublime_text', 'platforms']) self.assertIn(key, ('details', 'sublime_text', 'platforms'))
if key in ['details', 'url']: if key == 'details':
assert_regexp_matches(data[key], '^https?://') self.assertRegex(data[key], '^https?://')
if key == 'sublime_text': if key == 'sublime_text':
assert_regexp_matches(data[key], '^(\*|<=?\d{4}|>=?\d{4})$') self.assertRegex(data[key], '^(\*|<=?\d{4}|>=?\d{4})$')
if key == 'platforms': if key == 'platforms':
assert_in(type(data[key]), [str, list]) self.assertIsInstance(data[key], (str, list))
if type(data[key]) == str: if isinstance(data[key], str):
assert_in(data[key], ['*', 'osx', 'linux', 'windows']) self.assertIn(data[key], ('*', 'osx', 'linux', 'windows'))
else: else:
for platform in data[key]: for plat in data[key]:
assert_in(platform, ['*', 'osx', 'linux', 'windows']) self.assertIn(plat, ('*', 'osx', 'linux', 'windows'))
assert_in('details', data, 'A release must have a "details" key if it is in ' + \
'the main repository. For custom releases, a custom repository.json ' + \
'file must be hosted elsewhere.')
def _test_error(self, msg, e):
self.fail("%s: %r" % (msg, e))
def map_key_type(key):
return {
'name': str,
'details': str,
'releases': list,
'homepage': str,
'author': str,
'readme': str,
'issues': str,
'donate': str,
'buy': str,
'previous_names': list,
'labels': list
}.get(key)
################################################################################
if __name__ == '__main__': if __name__ == '__main__':
# Manually go up one directory if this file is run explicitly
if not os.path.exists(repo_file):
repo_file = os.path.join("..", repo_file)
unittest.main() unittest.main()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment