XZise has submitted this change and it was merged.
Change subject: Test metaclass ......................................................................
Test metaclass
Continues: I6ee69f5b4e594a111d91bc0bafb59716900b478c
A new metaclass creates classes with a consist set of flags, allowing nosetests selectors like '!write', '!user', 'wikibase', and 'family=wikipedia'.
The metaclass adds mixins for features needed by tests, and allow tests to be re-run across a declared list of sites.
Remove most of the globals from the test modules, and have each test declare which sites they access.
Add support for write tests, with new write tests against test.wikidata.org. Write tests are disabled by default.
tests/README.rst started with basic documentation of using and writing tests.
Bug: 56959 Bug: 56960 Bug: 70336 Change-Id: Id4b1fbeb68b4e4c17553b864fd89c160fc8feac1 --- A tests/README.rst M tests/__init__.py M tests/api_tests.py M tests/archivebot_tests.py A tests/aspects.py M tests/date_tests.py M tests/dry_api_tests.py M tests/dry_site_tests.py M tests/edit_failure_tests.py M tests/file_tests.py M tests/http_tests.py M tests/i18n_tests.py M tests/ipregex_tests.py M tests/namespace_tests.py M tests/page_tests.py M tests/pagegenerators_tests.py M tests/pwb_tests.py M tests/script_tests.py M tests/site_tests.py M tests/textlib_tests.py M tests/timestripper_tests.py M tests/utils.py M tests/weblib_tests.py A tests/wikibase_edit_tests.py M tests/wikibase_tests.py M tests/wikidataquery_tests.py M tests/xmlreader_tests.py 27 files changed, 1,360 insertions(+), 455 deletions(-)
Approvals: XZise: Looks good to me, approved
diff --git a/tests/README.rst b/tests/README.rst new file mode 100644 index 0000000..742018e --- /dev/null +++ b/tests/README.rst @@ -0,0 +1,127 @@ +=============== +Pywikibot tests +=============== + +The Pywikibot tests are based on the unittest framework +https://docs.python.org/2/library/unittest.html, +and are compatible with nose.https://nose.readthedocs.org/ + +The tests package provides a function load_tests that supports the +'load tests protocol'. +https://docs.python.org/2/library/unittest.html#load-tests-protocol. +The default ordering begins with tests of underlying components, then tests +site and page semantics, and finishes with tests of the scripts and tests +which are not yet included in the ordered tests. + +A function collector also exists in the 'tests' package. + +Running tests +============= + +All tests +--------- + +The entire suite of tests may be run in three ways from the root directory: + +setup.py +~~~~~~~~ + +:: + + python setup.py test + +Module unittest +~~~~~~~~~~~~~~~ + +:: + + python -m unittest -v + +nose +~~~~ + +:: + + nosetests -v + +Specific tests +-------------- + +Individual test components can be run using unittest, nosetests, or pwb + +unittest +~~~~~~~~ + +:: + + python -m unittest -v tests.site_tests + +nose +~~~~ + +:: + + nosetests -v tests.site_tests + +pwb +~~~ + +:: + + python pwb.py tests/site_tests.py -v + + +Contributing tests +================== + +Test modules should be named according to the pywikibot that is being tested. +e.g. the module pywikibot.page is tested by tests.page_tests. + +New test classes should be added to the existing test modules unless it +tests a new component of pywikibot. + +All test classes must be a subclass of tests.aspects.TestCase, which uses a +metaclass to dynamically check the test can be run on a specified site, or +run a test on multiple sites. + +Test sites +---------- + +If a test depends on a specific site, add class attributes 'family' and code'. + +:: + + family = 'wikipedia' + code = 'en' + +Once declared, the Site object can be accessed at self.site. + + +If a test requires multiple specific sites, add a class attribute 'sites'. + +:: + + sites = { + 'enwiki': { + 'family': 'wikipedia', + 'code': 'en', + }, + 'itwikt': { + 'family': 'wiktionary', + 'code': 'it', + } + } + +To obtain the Site object, call self.get_site with the key given to the site. + +:: + + self.get_site('itwikt') + +Other class attributes +---------------------- + +- 'net = False' : test class does not use a site +- 'user = True' : test class needs to login to site +- 'write = True' : test class needs to write to a site + diff --git a/tests/__init__.py b/tests/__init__.py index 4670c9b..9f9782e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -172,6 +172,10 @@ lambda *args: CachedRequest._make_dir(_cache_dir))
+cache_misses = 0 +cache_hits = 0 + + class TestRequest(CachedRequest):
"""Add caching to every Request except logins.""" @@ -186,12 +190,17 @@ def _load_cache(self): """Return whether the cache can be used.""" if not super(TestRequest, self)._load_cache(): + global cache_misses + cache_misses += 1 return False
if 'lgpassword' in self._uniquedescriptionstr(): self._data = None return False
+ global cache_hits + cache_hits += 1 + return True
def _write_cache(self, data): diff --git a/tests/api_tests.py b/tests/api_tests.py index f595a2b..3ff2cf6 100644 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -9,16 +9,18 @@ import datetime import pywikibot import pywikibot.data.api as api -from tests.utils import unittest -from tests.utils import SiteTestCase, CachedTestCase - -mysite = pywikibot.Site('en', 'wikipedia') +from tests.aspects import unittest, TestCase
-class TestApiFunctions(CachedTestCase): +class TestApiFunctions(TestCase): + + family = 'wikipedia' + code = 'en' + cached = True
def testObjectCreation(self): """Test that api.Request() creates an object with desired attributes""" + mysite = self.get_site() req = api.Request(site=mysite, action="test", foo="", bar="test") self.assertTrue(req) self.assertEqual(req.site, mysite) @@ -35,9 +37,16 @@ self.assertEqual(len(item), 2, item)
-class TestPageGenerator(CachedTestCase): +class TestPageGenerator(TestCase): + + family = 'wikipedia' + code = 'en' + + cached = True + def setUp(self): super(TestPageGenerator, self).setUp() + mysite = self.get_site() self.gen = api.PageGenerator(site=mysite, generator="links", titles="User:R'n'B") @@ -67,6 +76,7 @@ """Test that PageGenerator yields pages with expected attributes.""" titles = ["Broadcaster.com", "Broadcaster (definition)", "Wiktionary", "Wikipedia:Disambiguation"] + mysite = self.get_site() results = [p for p in self.gen] self.assertEqual(len(results), 4) for page in results: @@ -109,8 +119,20 @@ self.assertEqual(len(results), 4) # total=-1 but 4 expected
-class TestCachedRequest(SiteTestCase): +class TestCachedRequest(TestCase): + + """Test API Request caching. + + This test class does not use the forced test caching. + """ + + family = 'wikipedia' + code = 'en' + + cached = False + def testResults(self): + mysite = self.get_site() # Run the cached query twice to ensure the # data returned is equal params = {'action': 'query', diff --git a/tests/archivebot_tests.py b/tests/archivebot_tests.py index 2a11160..247940d 100644 --- a/tests/archivebot_tests.py +++ b/tests/archivebot_tests.py @@ -7,9 +7,12 @@ # __version__ = '$Id$'
+from datetime import datetime import pywikibot import pywikibot.page -from tests.utils import unittest, PywikibotTestCase +from pywikibot.textlib import TimeStripper +from scripts import archivebot +from tests.aspects import unittest, TestCase
THREADS = { 'als': 4, 'ar': 1, 'bar': 0, 'bg': 0, 'bjn': 1, 'bs': 0, 'ca': 5, 'ckb': 2, @@ -21,76 +24,54 @@ }
-class TestArchiveBotMeta(type): +class TestArchiveBot(TestCase):
- """Test meta class""" + """Test archivebot script on 40+ Wikipedia sites."""
- def __new__(cls, name, bases, dct): - """create the new class""" + family = 'wikipedia' + sites = dict([(code, {'family': 'wikipedia', 'code': code}) + for code in THREADS])
- def test_method(code): + cached = True
- def test_archivebot(self): - """Test archivebot for one site.""" - site = pywikibot.Site(code, 'wikipedia') - page = pywikibot.Page(site, 'user talk:xqt') - talk = archivebot.DiscussionPage(page, None) - self.assertTrue(isinstance(talk.archives, dict)) - self.assertTrue(isinstance(talk.archived_threads, int)) - self.assertTrue(talk.archiver is None) - self.assertTrue(isinstance(talk.header, basestring)) - self.assertTrue(isinstance(talk.timestripper, TimeStripper)) + def test_archivebot(self, code=None): + """Test archivebot for one site.""" + site = self.get_site(code) + page = pywikibot.Page(site, 'user talk:xqt') + talk = archivebot.DiscussionPage(page, None) + self.assertTrue(isinstance(talk.archives, dict)) + self.assertTrue(isinstance(talk.archived_threads, int)) + self.assertTrue(talk.archiver is None) + self.assertTrue(isinstance(talk.header, basestring)) + self.assertTrue(isinstance(talk.timestripper, TimeStripper))
- self.assertTrue(isinstance(talk.threads, list)) - self.assertGreaterEqual( - len(talk.threads), THREADS[code], - u'%d Threads found on %s,\n%d or more expected' - % (len(talk.threads), talk, THREADS[code])) + self.assertTrue(isinstance(talk.threads, list)) + self.assertGreaterEqual( + len(talk.threads), THREADS[code], + u'%d Threads found on %s,\n%d or more expected' + % (len(talk.threads), talk, THREADS[code]))
- for thread in talk.threads: - self.assertTrue(isinstance(thread, - archivebot.DiscussionThread)) - self.assertTrue(isinstance(thread.title, basestring)) - self.assertTrue(isinstance(thread.now, datetime)) - self.assertTrue(thread.now == talk.now) - self.assertTrue(isinstance(thread.ts, TimeStripper)) - self.assertTrue(thread.ts == talk.timestripper) - self.assertTrue(isinstance(thread.code, basestring)) - self.assertEqual(thread.code, talk.timestripper.site.code) - self.assertTrue(isinstance(thread.content, basestring)) - self.assertTrue(isinstance(thread.timestamp, datetime)) + for thread in talk.threads: + self.assertTrue(isinstance(thread, + archivebot.DiscussionThread)) + self.assertTrue(isinstance(thread.title, basestring)) + self.assertTrue(isinstance(thread.now, datetime)) + self.assertTrue(thread.now == talk.now) + self.assertTrue(isinstance(thread.ts, TimeStripper)) + self.assertTrue(thread.ts == talk.timestripper) + self.assertTrue(isinstance(thread.code, basestring)) + self.assertEqual(thread.code, talk.timestripper.site.code) + self.assertTrue(isinstance(thread.content, basestring)) + self.assertTrue(isinstance(thread.timestamp, datetime))
- return test_archivebot - - # setUp class - from datetime import datetime - from scripts import archivebot - from pywikibot.textlib import TimeStripper - - # create test methods processed by unittest - for code in THREADS: - test_name = "test_wikipedia_" + code - - if code in ['ar', 'ckb', 'fa', 'pdc', 'th']: - # expected failures - should be fixed - # 'ar', 'ckb', 'fa': no digits in date, regex does not match - # 'pdc': changed month name setting in wiki over time (?) - # in old posts in talk page, February is "Feb.", site message gives - # <message name="feb" xml:space="preserve">Han.</message>. - # for new entries it should work - # 'th': year is 2552 while regex assumes 19..|20.., might be fixed - dct[test_name] = unittest.expectedFailure(test_method(code)) - else: - dct[test_name] = test_method(code) - dct[test_name].__name__ = test_name - return type.__new__(cls, name, bases, dct) - - -class TestArchiveBot(PywikibotTestCase): - - """Test archivebot script.""" - - __metaclass__ = TestArchiveBotMeta + expected_failures = ['ar', 'ckb', 'fa', 'pdc', 'th'] + # expected failures - should be fixed + # 'ar', 'ckb', 'fa': no digits in date, regex does not match + # 'pdc': changed month name setting in wiki over time (?) + # in old posts in talk page, February is "Feb.", site message gives + # <message name="feb" xml:space="preserve">Han.</message>. + # for new entries it should work + # 'th': year is 2552 while regex assumes 19..|20.., might be fixed
if __name__ == '__main__': diff --git a/tests/aspects.py b/tests/aspects.py new file mode 100644 index 0000000..ccbb273 --- /dev/null +++ b/tests/aspects.py @@ -0,0 +1,526 @@ +# -*- coding: utf-8 -*- +""" +Test aspects to allow fine grained control over what tests are executed. + +Several parts of the test infrastructure are implemented as mixins, +such as API result caching and excessive test durations. An unused +mixin to show cache usage is included. +""" +# +# (C) Pywikibot team, 2014 +# +# Distributed under the terms of the MIT license. +# +from __future__ import print_function +__version__ = '$Id$' +""" + TODO: + + skip if the user is blocked. + sysop flag, implement in site & page, and + possibly some of the script tests. + wikimedia flag + labs flag, for wikidataquery + slow flag + wikiquerydata - quite slow + weblib - also slow + (this class, and a FastTest, could error/pass based + it consumed more than a specified amount of time allowed.) + net flag should disable network libraries + UITestCase: + Not integrated; direct subclass of unittest.TestCase. +""" +import time +import sys +import os + +import pywikibot + +from pywikibot import config, Site +from pywikibot.site import BaseSite + +import tests +from tests import unittest, patch_request, unpatch_request + + +class TestCaseBase(object): + + """Base class for all tests.""" + + pass + + +class TestTimerMixin(TestCaseBase): + + """Time each test and report excessive durations.""" + + # Number of seconds each test may consume + # before a note is added after the test. + test_duration_warning_interval = 10 + + def setUp(self): + """Set up test.""" + super(TestTimerMixin, self).setUp() + self.test_start = time.time() + + def tearDown(self): + """Tear down test.""" + self.test_completed = time.time() + duration = self.test_completed - self.test_start + + if duration > self.test_duration_warning_interval: + print(' %0.3fs' % duration, end=' ') + sys.stdout.flush() + + super(TestTimerMixin, self).tearDown() + + +class DisableSiteMixin(TestCaseBase): + + """Test cases not connected to a Site object. + + Do not use this for mock Site objects. + + Never set a class or instance variable called 'site' + As it will prevent tests from executing when invoked as: + $ nosetests -a '!site' -v + """ + + def setUp(self): + """Set up test.""" + self.old_Site_lookup_method = pywikibot.Site + pywikibot.Site = lambda *args: self.fail('%s: Site() not permitted' + % self.__class__.__name__) + + super(DisableSiteMixin, self).setUp() + + def tearDown(self): + """Tear down test.""" + super(DisableSiteMixin, self).tearDown() + + pywikibot.Site = self.old_Site_lookup_method + + +class ForceCacheMixin(object): + + """Aggressively cached API test cases. + + Patches pywikibot.data.api to aggressively cache + API responses. + """ + + def setUp(self): + """Set up test.""" + patch_request() + + super(ForceCacheMixin, self).setUp() + + def tearDown(self): + """Tear down test.""" + super(ForceCacheMixin, self).tearDown() + + unpatch_request() + + +class CacheInfoMixin(object): + + """Report cache hits and misses.""" + + def setUp(self): + """Set up test.""" + super(CacheInfoMixin, self).setUp() + self.cache_misses_start = tests.cache_misses + self.cache_hits_start = tests.cache_hits + + def tearDown(self): + """Tear down test.""" + self.cache_misses = tests.cache_misses - self.cache_misses_start + self.cache_hits = tests.cache_hits - self.cache_hits_start + + if self.cache_misses: + print(' %d cache misses' % self.cache_misses, end=' ') + if self.cache_hits: + print(' %d cache hits' % self.cache_hits, end=' ') + + if self.cache_misses or self.cache_hits: + sys.stdout.flush() + + super(CacheInfoMixin, self).tearDown() + + +class SiteWriteMixin(TestCaseBase): + + """ + Test cases involving writing to the server. + + When editing, the API should not be patched to use + CachedRequest. This class prevents that. + """ + + @classmethod + def setUpClass(cls): + """ + Set up the test class. + + Prevent test classes to write to the site and also cache results. + + Skip the test class if environment variable PYWIKIBOT2_TEST_WRITE + does not equal 1. + """ + if os.environ.get('PYWIKIBOT2_TEST_WRITE', '0') != '1': + raise unittest.SkipTest( + '%r write tests disabled. ' + 'Set PYWIKIBOT2_TEST_WRITE=1 to enable.' + % cls.__name__) + + if issubclass(cls, ForceCacheMixin): + raise Exception( + '%s can not be a subclass of both ' + 'SiteEditTestCase and ForceCacheMixin' + % cls.__name__) + + super(SiteWriteMixin, cls).setUpClass() + + +class RequireUserMixin(TestCaseBase): + + """Run tests against a specific site, with a login.""" + + user = True + + @classmethod + def require_site_user(cls): + """Check the user config has a valid login to the site.""" + if not cls.has_site_user(cls.family, cls.code): + raise unittest.SkipTest( + '%s: No username for %s:%s' + % (cls.__name__, cls.family, cls.code)) + + @classmethod + def setUpClass(cls): + """ + Set up the test class. + + Skip the test class if the user config does not have + a valid login to the site. + """ + super(RequireUserMixin, cls).setUpClass() + + cls.require_site_user() + + cls.site.login() + + if not cls.site.user(): + raise unittest.SkipTest( + '%s: Unable able to login to %s' + % cls.__name__, cls.site) + + +class MetaTestCaseClass(type): + + """Test meta class.""" + + def __new__(cls, name, bases, dct): + """Create the new class.""" + def wrap_method(key, sitedata, func): + + def wrapped_method(self): + sitedata = self.sites[key] + self.family = sitedata['family'] + self.code = sitedata['code'] + self.site = sitedata['site'] + func(self, key) + + sitename = sitedata['family'] + ':' + sitedata['code'] + if func.__doc__: + if func.__doc__.endswith('.'): + wrapped_method.__doc__ = func.__doc__[:-1] + else: + wrapped_method.__doc__ = func.__doc__ + wrapped_method.__doc__ += ' on ' + sitename + else: + wrapped_method.__doc__ = 'Test ' + sitename + + return wrapped_method + + tests = [attr_name + for attr_name in dct + if attr_name.startswith('test')] + + dct['abstract_class'] = len(tests) == 0 + + # Bail out if it is the abstract class. + if dct['abstract_class']: + return super(MetaTestCaseClass, cls).__new__(cls, name, bases, dct) + + # Inherit superclass attributes + for base in bases: + for key in ('pwb', 'net', 'site', 'wikibase', 'user', 'write', + 'sites', 'family', 'code', + 'cached', 'cacheinfo'): + if hasattr(base, key) and key not in dct: + # print('%s has %s; copying to %s' + # % (base.__name__, key, name)) + dct[key] = getattr(base, key) + + if 'pwb' in dct and dct['pwb']: + dct['spawn'] = True + if 'site' not in dct: + raise Exception( + '%s: Test classes using pwb must set "site"' + % name) + + if 'net' in dct and dct['net'] is False: + dct['site'] = False + + if 'sites' in dct and 'site' not in dct: + dct['site'] = True + + # If either are specified, assume both should be specified + if 'family' in dct or 'code' in dct: + dct['site'] = True + + if (('sites' not in dct or not len(dct['sites'])) + and 'family' in dct + and 'code' in dct and dct['code'] != '*'): + # Add entry to self.sites + dct['sites'] = { + str(dct['family'] + ':' + dct['code']): { + 'code': dct['code'], + 'family': dct['family'], + } + } + + if (('sites' not in dct and 'site' not in dct) or + ('site' in dct and not dct['site'])): + # Prevent use of pywikibot.Site + bases = tuple([DisableSiteMixin] + list(bases)) + + # If the 'site' attribute is a false value, + # remove it so it matches !site in nose. + if 'site' in dct: + del dct['site'] + + # If there isn't a site, require declaration of net activity. + if 'net' not in dct: + raise Exception( + '%s: Test classes without a site configured must set "net"' + % name) + + # If the 'net' attribute is a false value, + # remove it so it matches !net in nose. + if not dct['net']: + del dct['net'] + + return super(MetaTestCaseClass, cls).__new__(cls, name, bases, dct) + + if 'cacheinfo' in dct and dct['cacheinfo']: + bases = tuple([CacheInfoMixin] + list(bases)) + + if 'cached' in dct and dct['cached']: + bases = tuple([ForceCacheMixin] + list(bases)) + + if 'write' in dct and dct['write']: + bases = tuple([SiteWriteMixin] + list(bases)) + + if 'user' in dct and dct['user']: + bases = tuple([RequireUserMixin] + list(bases)) + + for test in tests: + test_func = dct[test] + + if test_func.__code__.co_argcount == 1: + continue + + if test_func.__code__.co_argcount != 2: + raise Exception( + '%s: Test method %s must accept either 1 or 2 arguments; ' + ' %d found' + % (name, test, test_func.__code__.co_argcount)) + + # create test methods processed by unittest + for (key, sitedata) in dct['sites'].items(): + test_name = test + '_' + key + + dct[test_name] = wrap_method(key, sitedata, dct[test]) + + if key in dct.get('expected_failures', []): + dct[test_name] = unittest.expectedFailure(dct[test_name]) + + del dct[test] + + return super(MetaTestCaseClass, cls).__new__(cls, name, bases, dct) + + +class TestCase(TestTimerMixin, TestCaseBase, unittest.TestCase): + + """Run tests on multiple sites.""" + + __metaclass__ = MetaTestCaseClass + + @classmethod + def setUpClass(cls): + """ + Set up the test class. + + Prefetch the Site object for each of the sites the test + class has declared are needed. + """ + super(TestCase, cls).setUpClass() + + if not hasattr(cls, 'sites'): + return + + # This stores the site under the site name. + if not cls.sites: + cls.sites = {} + + for data in cls.sites.values(): + if 'site' not in data: + site = Site(data['code'], data['family']) + data['site'] = site + + if len(cls.sites) == 1: + key = next(iter(cls.sites.keys())) + cls.site = cls.sites[key]['site'] + + @classmethod + def get_site(cls, name=None): + """Return the prefetched Site object.""" + if not name and hasattr(cls, 'sites'): + if len(cls.sites) == 1: + name = next(iter(cls.sites.keys())) + else: + raise Exception( + '"%s.get_site(name=None)" called with multiple sites' + % cls.__name__) + + if name and name not in cls.sites: + raise Exception('"%s" not declared in %s' + % (name, cls.__name__)) + + if isinstance(cls.site, BaseSite): + assert(cls.sites[name]['site'] == cls.site) + return cls.site + + return cls.sites[name]['site'] + + @classmethod + def has_site_user(cls, family, code): + """Check the user config has a user for the site.""" + if not family: + raise Exception('no family defined for %s' % cls.__name__) + if not code: + raise Exception('no site code defined for %s' % cls.__name__) + + return code in config.usernames[family] or \ + '*' in config.usernames[family] + + def __init__(self, *args, **kwargs): + """Constructor.""" + super(TestCase, self).__init__(*args, **kwargs) + + if not hasattr(self, 'sites'): + return + + # Create an instance method named the same as the class method + self.get_site = lambda name=None: self.__class__.get_site(name) + + +if sys.version_info[0] > 2: + import six + TestCase = six.add_metaclass(MetaTestCaseClass)(TestCase) + + +class DefaultSiteTestCase(TestCase): + + """Run tests against the config specified site.""" + + family = config.family + code = config.mylang + + +class WikibaseTestCase(TestCase): + + """Run tests against a wikibase site.""" + + wikibase = True + + @classmethod + def setUpClass(cls): + """ + Set up the test class. + + Checks that all sites are configured with a Wikibase repository, + with Site.has_data_repository() returning True, and all sites + use the same data repository. + """ + super(WikibaseTestCase, cls).setUpClass() + + for site in cls.sites.values(): + if not site['site'].has_data_repository: + raise unittest.SkipTest( + u'%s: %r does not have data repository' + % (cls.__name__, site['site'])) + + if (hasattr(cls, 'repo') and + cls.repo != site['site'].data_repository()): + raise Exception( + '%s: sites do not not all have the same data repository' + % cls.__name__) + + cls.repo = site['site'].data_repository() + + @classmethod + def get_repo(cls): + """Return the prefetched DataSite object.""" + return cls.repo + + def __init__(self, *args, **kwargs): + """Constructor.""" + super(TestCase, self).__init__(*args, **kwargs) + + if not hasattr(self, 'sites'): + return + + # Create an instance method named the same as the class method + self.get_repo = lambda: self.repo + + +class WikibaseClientTestCase(WikibaseTestCase): + + """Run tests against a specific site connected to a wikibase.""" + + @classmethod + def setUpClass(cls): + """ + Set up the test class. + + Checks that all sites are configured as a Wikibase client, + with Site.has_transcluded_data() returning True. + """ + super(WikibaseClientTestCase, cls).setUpClass() + + for site in cls.sites.values(): + if not site['site'].has_transcluded_data: + raise unittest.SkipTest( + u'%s: %r does not have transcluded data' + % (cls.__name__, site['site'])) + + +class WikidataTestCase(WikibaseTestCase): + + """Test cases use Wikidata.""" + + family = 'wikidata' + code = 'wikidata' + + cached = True + + +class PwbTestCase(TestCase): + + """Test cases use pwb.py to invoke scripts.""" + + pwb = True + spawn = True diff --git a/tests/date_tests.py b/tests/date_tests.py index 4991052..5d26688 100644 --- a/tests/date_tests.py +++ b/tests/date_tests.py @@ -6,16 +6,16 @@ # __version__ = '$Id$'
-from tests.utils import unittest, NoSiteTestCase from pywikibot import date +from tests.aspects import unittest, MetaTestCaseClass, TestCase
-class TestDateMeta(type): +class TestDateMeta(MetaTestCaseClass): + """Test meta class"""
def __new__(cls, name, bases, dct): """Create the new class""" - def test_method(formatname):
def testMapEntry(self): @@ -50,12 +50,14 @@ return type.__new__(cls, name, bases, dct)
-class TestDate(NoSiteTestCase): +class TestDate(TestCase):
"""Test cases for date library processed by unittest."""
__metaclass__ = TestDateMeta
+ net = False +
if __name__ == '__main__': try: diff --git a/tests/dry_api_tests.py b/tests/dry_api_tests.py index 83dac6e..990d579 100644 --- a/tests/dry_api_tests.py +++ b/tests/dry_api_tests.py @@ -10,14 +10,28 @@ import datetime import pywikibot from pywikibot.data.api import CachedRequest, QueryGenerator -from tests.utils import unittest, NoSiteTestCase, SiteTestCase, DummySiteinfo +from pywikibot.family import Family +from tests.utils import DummySiteinfo +from tests.aspects import unittest, TestCase, DefaultSiteTestCase
-class DryCachedRequestTests(SiteTestCase): +class DryCachedRequestTests(TestCase): + + sites = { + 'basesite': { + 'family': 'wikipedia', + 'code': 'en', + }, + 'altsite': { + 'family': 'wikipedia', + 'code': 'de', + }, + }
def setUp(self): - self.basesite = pywikibot.Site('en', 'wikipedia') - self.altsite = pywikibot.Site('de', 'wikipedia') + super(DryCachedRequestTests, self).setUp() + self.basesite = self.get_site('basesite') + self.altsite = self.get_site('altsite') self.parms = {'site': self.basesite, 'action': 'query', 'meta': 'userinfo'} @@ -25,8 +39,6 @@ self.expreq = CachedRequest(expiry=0, **self.parms) self.diffreq = CachedRequest(expiry=1, site=self.basesite, action='query', meta='siteinfo') self.diffsite = CachedRequest(expiry=1, site=self.altsite, action='query', meta='userinfo') - - super(DryCachedRequestTests, self).setUp()
def test_expiry_formats(self): self.assertEqual(self.req.expiry, CachedRequest(datetime.timedelta(days=1), **self.parms).expiry) @@ -51,9 +63,12 @@ self.assertNotEqual(self.req._cachefile_path(), self.diffsite._cachefile_path())
-class MockCachedRequestKeyTests(NoSiteTestCase): +class MockCachedRequestKeyTests(TestCase): + + net = False + def setUp(self): - class MockFamily(pywikibot.family.Family): + class MockFamily(Family):
@property def name(self): @@ -149,7 +164,7 @@ self.assertEqual(en_user_path, ar_user_path)
-class DryQueryGenTests(SiteTestCase): +class DryQueryGenTests(DefaultSiteTestCase):
def test_query_constructor(self): """Test QueryGenerator constructor. diff --git a/tests/dry_site_tests.py b/tests/dry_site_tests.py index c3e12aa..56a44dd 100644 --- a/tests/dry_site_tests.py +++ b/tests/dry_site_tests.py @@ -11,7 +11,8 @@ from pywikibot.site import must_be, need_version from pywikibot.comms.http import user_agent
-from tests.utils import unittest, NoSiteTestCase, DummySiteinfo +from tests.utils import DummySiteinfo +from tests.aspects import unittest, TestCase
class DrySite(pywikibot.site.APISite): @@ -26,7 +27,10 @@ return DummySiteinfo({})
-class TestDrySite(NoSiteTestCase): +class TestDrySite(TestCase): + + net = False + def test_logged_in(self): x = DrySite('en', 'wikipedia')
@@ -96,9 +100,11 @@ user_agent(x, format_string='Foo ({script_comments})'))
-class TestMustBe(NoSiteTestCase): +class TestMustBe(TestCase):
"""Test cases for the must_be decorator.""" + + net = False
# Implemented without setUpClass(cls) and global variables as objects # were not completely disposed and recreated but retained 'memory' @@ -161,10 +167,12 @@ self.assertRaises(pywikibot.NoSuchSite, self.call_this_user_req_function, args, kwargs)
-class TestNeedVersion(NoSiteTestCase): +class TestNeedVersion(TestCase):
"""Test cases for the need_version decorator."""
+ net = False + # Implemented without setUpClass(cls) and global variables as objects # were not completely disposed and recreated but retained 'memory' def setUp(self): diff --git a/tests/edit_failure_tests.py b/tests/edit_failure_tests.py index 50010b8..0dcfc60 100644 --- a/tests/edit_failure_tests.py +++ b/tests/edit_failure_tests.py @@ -22,13 +22,13 @@
class TestSaveFailure(SiteTestCase): + """Test cases for edits which should fail to save."""
write = True
- def setUp(self): - super(TestSaveFailure, self).setUp() - self.site = pywikibot.Site('test', 'wikipedia') + family = 'wikipedia' + code = 'test'
def test_protected(self): """Test that protected titles raise the appropriate exception.""" @@ -48,6 +48,7 @@ page = pywikibot.Page(self.site, 'User:John Vandenberg/nobots') self.assertRaisesRegexp(OtherPageSaveError, 'nobots', page.save)
+ if __name__ == '__main__': try: unittest.main() diff --git a/tests/file_tests.py b/tests/file_tests.py index dc4a3b3..3f58323 100644 --- a/tests/file_tests.py +++ b/tests/file_tests.py @@ -8,17 +8,37 @@
import pywikibot
-from tests.utils import unittest, SiteTestCase - -commons = pywikibot.Site('commons', 'commons') +from tests.aspects import unittest, TestCase
-class TestShareFiles(SiteTestCase): +class TestShareFiles(TestCase): + + sites = { + 'enwiki': { + 'family': 'wikipedia', + 'code': 'en', + }, + 'itwiki': { + 'family': 'wikipedia', + 'code': 'it', + }, + 'testwiki': { + 'family': 'wikipedia', + 'code': 'test', + }, + 'commons': { + 'family': 'commons', + 'code': 'commons', + }, + } + + cached = True
def testSharedOnly(self): title = 'File:Sepp Maier 1.JPG'
- itwp = pywikibot.Site('it', 'wikipedia') + commons = self.get_site('commons') + itwp = self.get_site('itwiki') itwp_file = pywikibot.ImagePage(itwp, title) for using in itwp_file.usingPages(): self.assertIsInstance(using, pywikibot.Page) @@ -38,7 +58,8 @@ def testLocalOnly(self): title = 'File:April Fools Day Adminship discussion (2005).png'
- enwp = pywikibot.Site('en', 'wikipedia') + commons = self.get_site('commons') + enwp = self.get_site('enwiki') enwp_file = pywikibot.ImagePage(enwp, title) for using in enwp_file.usingPages(): self.assertIsInstance(using, pywikibot.Page) @@ -58,7 +79,8 @@ def testOnBoth(self): title = 'File:Pulsante spam.png'
- itwp = pywikibot.Site('it', 'wikipedia') + commons = self.get_site('commons') + itwp = self.get_site('itwiki') itwp_file = pywikibot.ImagePage(itwp, title) for using in itwp_file.usingPages(): self.assertIsInstance(using, pywikibot.Page) @@ -76,7 +98,8 @@ """Test file page, without local file, existing on the local wiki.""" title = 'File:Sepp Maier 1.JPG'
- testwp = pywikibot.Site('test', 'wikipedia') + commons = self.get_site('commons') + testwp = self.get_site('testwiki') testwp_file = pywikibot.ImagePage(testwp, title)
self.assertTrue(testwp_file.fileUrl()) diff --git a/tests/http_tests.py b/tests/http_tests.py index 2c90b65..395c95f 100644 --- a/tests/http_tests.py +++ b/tests/http_tests.py @@ -11,10 +11,10 @@ import pywikibot from pywikibot.comms import http, threadedhttp from pywikibot import config2 as config -from tests.utils import unittest, NoSiteTestCase +from tests.aspects import unittest, TestCase
-class HttpTestCase(NoSiteTestCase): +class HttpTestCase(TestCase):
net = True
@@ -76,7 +76,9 @@ self.assertEqual('%E2%81%82', http.user_agent_username(u'⁂'))
-class DefaultUserAgentTestCase(NoSiteTestCase): +class DefaultUserAgentTestCase(TestCase): + + net = False
def setUp(self): self.orig_format = config.user_agent_format diff --git a/tests/i18n_tests.py b/tests/i18n_tests.py index 0bdc03f..2d33274 100644 --- a/tests/i18n_tests.py +++ b/tests/i18n_tests.py @@ -8,10 +8,13 @@
from pywikibot import i18n
-from tests.utils import unittest, NoSiteTestCase +from tests.aspects import unittest, TestCase
-class TestTranslate(NoSiteTestCase): +class TestTranslate(TestCase): + + net = False + def setUp(self): self.msg_localized = {'en': u'test-localized EN', 'nl': u'test-localized NL', @@ -70,7 +73,10 @@ u'test-no-english JA')
-class TestTWN(NoSiteTestCase): +class TestTWN(TestCase): + + net = False + def setUp(self): self.orig_messages_package_name = i18n.messages_package_name i18n.messages_package_name = 'tests.i18n' diff --git a/tests/ipregex_tests.py b/tests/ipregex_tests.py index 7eaaae2..0498561 100644 --- a/tests/ipregex_tests.py +++ b/tests/ipregex_tests.py @@ -6,13 +6,16 @@ # Distributed under the terms of the MIT license. __version__ = '$Id$'
-from tests.utils import unittest, NoSiteTestCase +from tests.aspects import unittest, TestCase from pywikibot.page import ip_regexp
-class PyWikiIpRegexCase(NoSiteTestCase): +class PyWikiIpRegexCase(TestCase): + """Unit test class for ip_regexp."""
+ net = False + def setUp(self): self.total = 0 self.fail = 0 diff --git a/tests/namespace_tests.py b/tests/namespace_tests.py index 130b0c9..73930e3 100644 --- a/tests/namespace_tests.py +++ b/tests/namespace_tests.py @@ -9,7 +9,7 @@
from collections import Iterable from pywikibot.site import Namespace -from tests.utils import NoSiteTestCase, unittest +from tests.aspects import unittest, TestCase
import sys if sys.version_info[0] > 2: @@ -17,10 +17,12 @@ unicode = str
-class TestNamespaceObject(NoSiteTestCase): +class TestNamespaceObject(TestCase):
"""Test cases for Namespace class."""
+ net = False + # These should work in any MW wiki builtin_ids = { 'Media': -2, diff --git a/tests/page_tests.py b/tests/page_tests.py index c6eecf0..9921407 100644 --- a/tests/page_tests.py +++ b/tests/page_tests.py @@ -11,28 +11,54 @@ import pywikibot import pywikibot.page
-from tests.utils import PywikibotTestCase, unittest +from tests.aspects import unittest, TestCase
if sys.version_info[0] > 2: basestring = (str, ) unicode = str
-site = pywikibot.Site('en', 'wikipedia') -mainpage = pywikibot.Page(pywikibot.page.Link("Main Page", site)) -maintalk = pywikibot.Page(pywikibot.page.Link("Talk:Main Page", site)) -badpage = pywikibot.Page(pywikibot.page.Link("There is no page with this title", - site)) +# These two globals are only used in TestPageObject. +site = None +mainpage = None
-class TestLinkObject(PywikibotTestCase): +class TestLinkObject(TestCase):
"""Test cases for Link objects."""
- enwiki = pywikibot.Site("en", "wikipedia") - frwiki = pywikibot.Site("fr", "wikipedia") - itwikt = pywikibot.Site("it", "wiktionary") - enws = pywikibot.Site("en", "wikisource") - itws = pywikibot.Site("it", "wikisource") + sites = { + 'enwiki': { + 'family': 'wikipedia', + 'code': 'en', + }, + 'frwiki': { + 'family': 'wikipedia', + 'code': 'fr', + }, + 'itwikt': { + 'family': 'wiktionary', + 'code': 'it', + }, + 'enws': { + 'family': 'wikisource', + 'code': 'en', + }, + 'itws': { + 'family': 'wikisource', + 'code': 'it', + }, + } + + cached = True + + @classmethod + def setUpClass(cls): + super(TestLinkObject, cls).setUpClass() + cls.enwiki = cls.get_site('enwiki') + cls.frwiki = cls.get_site('frwiki') + cls.itwikt = cls.get_site('itwikt') + cls.enws = cls.get_site('enws') + cls.itws = cls.get_site('itws')
namespaces = {0: [u""], # en.wikipedia.org namespaces for testing 1: [u"Talk:"], # canonical form first, then others @@ -134,7 +160,22 @@ self.assertRaises(pywikibot.Error, l3.ns_title, onsite=self.itws)
-class TestPageObject(PywikibotTestCase): +class TestPageObject(TestCase): + + family = 'wikipedia' + code = 'en' + + cached = True + + @classmethod + def setUpClass(cls): + global site, mainpage + super(TestPageObject, cls).setUpClass() + site = cls.get_site() + mainpage = pywikibot.Page(pywikibot.page.Link("Main Page", site)) + cls.maintalk = pywikibot.Page(pywikibot.page.Link("Talk:Main Page", site)) + cls.badpage = pywikibot.Page(pywikibot.page.Link( + "There is no page with this title", site))
def testGeneral(self): family_name = (site.family.name + ':' @@ -143,7 +184,7 @@ self.assertEqual(str(mainpage), "[[%s%s:%s]]" % (family_name, site.code, mainpage.title())) - self.assertLess(mainpage, maintalk) + self.assertLess(mainpage, self.maintalk)
def testSite(self): """Test site() method""" @@ -152,8 +193,8 @@ def testNamespace(self): """Test namespace() method""" self.assertEqual(mainpage.namespace(), 0) - self.assertEqual(maintalk.namespace(), 1) - self.assertEqual(badpage.namespace(), 0) + self.assertEqual(self.maintalk.namespace(), 1) + self.assertEqual(self.badpage.namespace(), 0)
def testTitle(self): """Test title() method options.""" @@ -309,17 +350,16 @@ """Test various methods that rely on API.""" # since there is no way to predict what data the wiki will return, # we only check that the returned objects are of correct type. - self.assertIsInstance(mainpage.get(), unicode) - self.assertIsInstance(maintalk.get(), unicode) - self.assertRaises(pywikibot.NoPage, badpage.get) + self.assertIsInstance(self.maintalk.get(), unicode) + self.assertRaises(pywikibot.NoPage, self.badpage.get) self.assertIsInstance(mainpage.latestRevision(), int) self.assertIsInstance(mainpage.userName(), unicode) self.assertIsInstance(mainpage.isIpEdit(), bool) self.assertIsInstance(mainpage.exists(), bool) self.assertIsInstance(mainpage.isRedirectPage(), bool) self.assertIsInstance(mainpage.isEmpty(), bool) - self.assertEqual(mainpage.toggleTalkPage(), maintalk) - self.assertEqual(maintalk.toggleTalkPage(), mainpage) + self.assertEqual(mainpage.toggleTalkPage(), self.maintalk) + self.assertEqual(self.maintalk.toggleTalkPage(), mainpage) self.assertIsInstance(mainpage.isDisambig(), bool) self.assertIsInstance(mainpage.canBeEdited(), bool) self.assertIsInstance(mainpage.botMayEdit(), bool) @@ -423,7 +463,10 @@ # def contributingUsers(self):
-class TestCategoryObject(PywikibotTestCase): +class TestCategoryObject(TestCase): + + site = True + cached = True
def test_isEmptyCategory(self): """Test if category is empty or not""" diff --git a/tests/pagegenerators_tests.py b/tests/pagegenerators_tests.py index 1bcfa34..96f574c 100755 --- a/tests/pagegenerators_tests.py +++ b/tests/pagegenerators_tests.py @@ -10,12 +10,17 @@ import pywikibot from pywikibot import pagegenerators
-from tests.utils import unittest, PywikibotTestCase +from tests.aspects import unittest, TestCase
-class TestPageGenerators(PywikibotTestCase): +class TestPageGenerators(TestCase):
"""Test pagegenerators methods.""" + + family = 'wikipedia' + code = 'en' + + cached = True
titles = [ # just a bunch of randomly selected titles @@ -35,8 +40,8 @@ ]
def setUp(self): - self.site = pywikibot.Site('en', 'wikipedia') super(TestPageGenerators, self).setUp() + self.site = self.get_site()
def assertFunction(self, obj): self.assertTrue(hasattr(pagegenerators, obj)) diff --git a/tests/pwb_tests.py b/tests/pwb_tests.py index deffdae..e725a9d 100644 --- a/tests/pwb_tests.py +++ b/tests/pwb_tests.py @@ -11,7 +11,7 @@ import subprocess import pywikibot
-from tests.utils import unittest, SiteTestCase +from tests.aspects import unittest, TestCase
pypath = sys.executable basepath = os.path.split(os.path.split(__file__)[0])[0] @@ -23,7 +23,7 @@ return subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0]
-class TestPwb(SiteTestCase): +class TestPwb(TestCase):
""" Test pwb.py functionality. @@ -32,6 +32,10 @@ without a user-config.py """
+ pwb = True + net = True + site = True + def setUp(self): self.oldenviron = os.environ.copy() os.environ['PYWIKIBOT2_DIR'] = pywikibot.config.base_dir diff --git a/tests/script_tests.py b/tests/script_tests.py index 9287a01..e2324c5 100644 --- a/tests/script_tests.py +++ b/tests/script_tests.py @@ -13,7 +13,7 @@ import subprocess import pywikibot from pywikibot import config -from tests.utils import unittest, PywikibotTestCase +from tests.aspects import unittest, DefaultSiteTestCase, MetaTestCaseClass, PwbTestCase
base_path = os.path.split(os.path.split(__file__)[0])[0] @@ -206,7 +206,7 @@ 'stderr': data_out[1]}
-class TestScriptMeta(type): +class TestScriptMeta(MetaTestCaseClass):
"""Test meta class."""
@@ -311,7 +311,7 @@ return type.__new__(cls, name, bases, dct)
-class TestScript(PywikibotTestCase): +class TestScript(DefaultSiteTestCase, PwbTestCase):
"""Test cases for scripts.
@@ -321,6 +321,8 @@
__metaclass__ = TestScriptMeta
+ site = True + def setUp(self): """Prepare the environment for running the pwb.py script.""" self.old_pywikibot_dir = None diff --git a/tests/site_tests.py b/tests/site_tests.py index 7eb526c..28948df 100644 --- a/tests/site_tests.py +++ b/tests/site_tests.py @@ -16,7 +16,7 @@
import pywikibot from pywikibot.exceptions import Error, NoPage -from tests.utils import SiteTestCase, PywikibotTestCase, unittest +from tests.aspects import unittest, TestCase
if sys.version_info[0] > 2: basestring = (str, ) @@ -27,17 +27,20 @@ imagepage = None
-class TestSiteObject(PywikibotTestCase): +class TestSiteObject(TestCase):
"""Test cases for Site methods."""
family = "wikipedia" code = "en"
+ cached = True + @classmethod def setUpClass(cls): global mysite, mainpage, imagepage - mysite = pywikibot.Site(cls.code, cls.family) + super(TestSiteObject, cls).setUpClass() + mysite = cls.get_site() mainpage = pywikibot.Page(pywikibot.Link("Main Page", mysite)) imagepage = next(iter(mainpage.imagelinks())) # 1st image on main page
@@ -1050,16 +1053,20 @@ page_from, 'Main Page', 'test')
-class TestSiteLoadRevisions(PywikibotTestCase): +class TestSiteLoadRevisions(TestCase):
"""Test cases for Site.loadrevision() method.""" + + family = 'wikipedia' + code = 'en' + + cached = True
# Implemented without setUpClass(cls) and global variables as objects # were not completely disposed and recreated but retained 'memory' def setUp(self): super(TestSiteLoadRevisions, self).setUp() - code, family = "en", "wikipedia" - self.mysite = pywikibot.Site(code, family) + self.mysite = self.get_site() self.mainpage = pywikibot.Page(pywikibot.Link("Main Page", self.mysite))
def testLoadRevisions_basic(self): @@ -1164,15 +1171,17 @@ # TODO test other optional arguments
-class TestCommonsSite(PywikibotTestCase): +class TestCommonsSite(TestCase):
"""Test cases for Site methods on Commons."""
family = "commons" code = "commons"
+ cached = True + def testInterWikiForward(self): - self.site = pywikibot.Site(self.code, self.family) + self.site = self.get_site() self.mainpage = pywikibot.Page(pywikibot.Link("Main Page", self.site)) # test pagelanglinks on commons, # which forwards interwikis to wikipedia @@ -1181,13 +1190,24 @@ self.assertEqual(ll.site.family.name, 'wikipedia')
-class TestUploadEnabledSite(SiteTestCase): +class TestUploadEnabledSite(TestCase): + + sites = { + 'wikidatatest': { + 'family': 'wikidata', + 'code': 'test', + }, + 'wikipediatest': { + 'family': 'wikipedia', + 'code': 'test', + } + }
def test_is_uploaddisabled(self): - site = pywikibot.Site('test', 'wikipedia') + site = self.get_site('wikipediatest') self.assertFalse(site.is_uploaddisabled())
- site = pywikibot.Site('test', 'wikidata') + site = self.get_site('wikidatatest') self.assertTrue(site.is_uploaddisabled())
diff --git a/tests/textlib_tests.py b/tests/textlib_tests.py index 11513f1..d4586e5 100644 --- a/tests/textlib_tests.py +++ b/tests/textlib_tests.py @@ -18,7 +18,7 @@ import pywikibot.textlib as textlib from pywikibot import config
-from tests.utils import unittest, NoSiteTestCase, PywikibotTestCase +from tests.aspects import unittest, TestCase
files = {} dirname = os.path.join(os.path.dirname(__file__), "pages") @@ -28,7 +28,10 @@ 'r', 'utf-8').read()
-class TestSectionFunctions(NoSiteTestCase): +class TestSectionFunctions(TestCase): + + net = False + def setUp(self): self.catresult1 = ('[[Category:Cat1]]%(LS)s[[Category:Cat2]]%(LS)s' % {'LS': config.LS}) @@ -92,11 +95,17 @@ self.assertNotContains("enwiki_help_editing", u"Helpful tips", "section header must contain a link")
-class TestFormatFunctions(PywikibotTestCase): +class TestFormatFunctions(TestCase): + + family = 'wikipedia' + code = 'en' + + cached = True
@classmethod def setUpClass(cls): - cls.site = pywikibot.Site('en', 'wikipedia') + super(TestFormatFunctions, cls).setUpClass() + cls.site = cls.get_site() cls.catresult = ('[[Category:Cat1]]%(LS)s[[Category:Cat2]]%(LS)s' % {'LS': config.LS})
@@ -132,7 +141,7 @@ textlib.categoryFormat(data, self.site))
-class TestCategoryRearrangement(PywikibotTestCase): +class TestCategoryRearrangement(TestCase):
""" Tests to ensure that sorting keys are not being lost when @@ -140,9 +149,15 @@ with both a newline and an empty string as separators. """
+ family = 'wikipedia' + code = 'en' + + cached = True + @classmethod def setUpClass(cls): - cls.site = pywikibot.Site('en', 'wikipedia') + super(TestCategoryRearrangement, cls).setUpClass() + cls.site = cls.get_site() cls.old = ('[[Category:Cat1]]%(LS)s[[Category:Cat2|]]%(LS)s' '[[Category:Cat1| ]]%(LS)s[[Category:Cat2|key]]' % {'LS': config.LS}) @@ -163,15 +178,17 @@ config.line_separator = sep # restore the default separator
-class TestTemplatesInCategory(PywikibotTestCase): +class TestTemplatesInCategory(TestCase):
"""Tests to verify that templates in category links are handled."""
- @classmethod - def setUpClass(cls): - cls.site = pywikibot.Site('en', 'wikipedia') + family = 'wikipedia' + code = 'en' + + cached = True
def test_templates(self): + self.site = self.get_site() self.assertEqual(textlib.getCategoryLinks( '[[Category:{{P1|Foo}}]]', self.site), [pywikibot.page.Category(self.site, 'Foo')]) diff --git a/tests/timestripper_tests.py b/tests/timestripper_tests.py index d3b1ff6..d625c4b 100644 --- a/tests/timestripper_tests.py +++ b/tests/timestripper_tests.py @@ -9,19 +9,22 @@
import datetime
-import pywikibot -from tests.utils import PywikibotTestCase, unittest from pywikibot.textlib import TimeStripper, tzoneFixedOffset +from tests.aspects import unittest, TestCase
-class TestTimeStripperWithNoDigitsAsMonths(PywikibotTestCase): +class TestTimeStripperWithNoDigitsAsMonths(TestCase):
"""Test cases for TimeStripper methods."""
+ family = 'wikipedia' + code = 'fr' + + cached = True + def setUp(self): - site = pywikibot.Site('fr', 'wikipedia') - self.ts = TimeStripper(site) super(TestTimeStripperWithNoDigitsAsMonths, self).setUp() + self.ts = TimeStripper(self.get_site())
def test_findmarker(self): """Test that string which is not part of text is found.""" @@ -72,33 +75,27 @@ None) )
- def test_timestripper(self): - """Test that correct date is matched.""" - txtMatch = u'3 février 2010 à 19:48 (CET) 7 février 2010 à 19:48 (CET)' - txtNoMatch = u'3 March 2010 19:48 (CET) 7 March 2010 19:48 (CET)' + def test_hour(self): + """Test that correct hour is matched.""" txtHourInRange = u'7 février 2010 à 23:00 (CET)' txtHourOutOfRange = u'7 février 2010 à 24:00 (CET)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) - - res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone) - - self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None)
self.assertNotEqual(self.ts.timestripper(txtHourInRange), None) self.assertEqual(self.ts.timestripper(txtHourOutOfRange), None)
-class TestTimeStripperWithDigitsAsMonths(PywikibotTestCase): +class TestTimeStripperWithDigitsAsMonths(TestCase):
- """Test cases for TimeStripper methods.""" + """Test cases for TimeStripper methods""" + + family = 'wikipedia' + code = 'cs' + + cached = True
def setUp(self): - site = pywikibot.Site('cs', 'wikipedia') - self.ts = TimeStripper(site) super(TestTimeStripperWithDigitsAsMonths, self).setUp() + self.ts = TimeStripper(self.get_site())
def test_last_match_and_replace(self): """Test that pattern matches and removes items correctly.""" @@ -125,144 +122,97 @@ None) )
- def test_timestripper(self): - txtMatch = u'3. 2. 2010, 19:48 (UTC) 7. 2. 2010 19:48 (UTC)' - txtNoMatch = u'3 March 2010 19:48 (UTC) 7 March 2010 19:48 (UTC)' + +class TestTimeStripperLanguage(TestCase): + + """Test cases for English language""" + + sites = { + 'cswiki': { + 'family': 'wikipedia', + 'code': 'cs', + 'match': u'3. 2. 2010, 19:48 (UTC) 7. 2. 2010 19:48 (UTC)', + }, + 'enwiki': { + 'family': 'wikipedia', + 'code': 'en', + 'match': u'3 February 2010 19:48 (UTC) 7 February 2010 19:48 (UTC)', + 'nomatch': u'3. 2. 2010, 19:48 (UTC) 7. 2. 2010 19:48 (UTC)', + }, + 'frwiki': { + 'family': 'wikipedia', + 'code': 'fr', + 'match': u'3 février 2010 à 19:48 (CET) 7 février 2010 à 19:48 (CET)', + 'nomatch': u'3 March 2010 19:48 (CET) 7 March 2010 19:48 (CET)', + }, + 'nowiki': { + 'family': 'wikipedia', + 'code': 'no', + 'match': u'3. feb 2010 kl. 19:48 (CET) 7. feb 2010 kl. 19:48 (UTC)', + }, + 'ptwiki': { + 'family': 'wikipedia', + 'code': 'pt', + 'match': u'19h48min de 3 de fevereiro de 2010 (UTC) 19h48min de 7 de fevereiro de 2010 (UTC)', + }, + 'viwiki': { + 'family': 'wikipedia', + 'code': 'vi', + 'match': u'19:48, ngày 15 tháng 9 năm 2008 (UTC) 19:48, ngày 7 tháng 2 năm 2010 (UTC)', + 'match2': u'16:41, ngày 15 tháng 9 năm 2008 (UTC) 16:41, ngày 12 tháng 9 năm 2008 (UTC)', + 'match3': u'21:18, ngày 13 tháng 8 năm 2014 (UTC) 21:18, ngày 14 tháng 8 năm 2014 (UTC)', + 'nomatch1': u'21:18, ngày 13 March 8 năm 2014 (UTC) 21:18, ngày 14 March 8 năm 2014 (UTC)', + }, + } + + cached = True + + def test_timestripper_match(self, key): + """Test that correct date is matched.""" + self.ts = TimeStripper(self.get_site(key))
tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], self.ts.site.siteinfo['timezone']) + + txtMatch = self.sites[key]['match']
res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone)
self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None)
+ if 'match2' not in self.sites[key]: + return
-class TestEnglishTimeStripper(PywikibotTestCase): - - """Test cases for English language.""" - - def setUp(self): - site = pywikibot.Site('en', 'wikipedia') - self.ts = TimeStripper(site) - super(TestEnglishTimeStripper, self).setUp() - - def test_timestripper(self): - """Test that correct date is matched.""" - txtMatch = u'3 February 2010 19:48 (UTC) 7 February 2010 19:48 (UTC)' - txtNoMatch = u'3. 2. 2010, 19:48 (UTC) 7. 2. 2010 19:48 (UTC)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) - - res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone) - - self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None) - - -class TestCzechTimeStripper(PywikibotTestCase): - - """Test cases for Czech language.""" - - def setUp(self): - site = pywikibot.Site('cs', 'wikipedia') - self.ts = TimeStripper(site) - super(TestCzechTimeStripper, self).setUp() - - def test_timestripper(self): - """Test that correct date is matched.""" - txtMatch = u'3. 2. 2010, 19:48 (UTC) 7. 2. 2010 19:48 (UTC)' - txtNoMatch = u'3 March 2010 19:48 (UTC) 7 March 2010 19:48 (UTC)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) - - res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone) - - self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None) - - -class TestPortugueseTimeStripper(PywikibotTestCase): - - """Test cases for Portuguese language.""" - - def setUp(self): - site = pywikibot.Site('pt', 'wikipedia') - self.ts = TimeStripper(site) - super(TestPortugueseTimeStripper, self).setUp() - - def test_timestripper(self): - """Test that correct date is matched.""" - txtMatch = u'19h48min de 3 de fevereiro de 2010 (UTC) 19h48min de 7 de fevereiro de 2010 (UTC)' - txtNoMatch = u'3 March 2010 19:48 (UTC) 7 March 2010 19:48 (UTC)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) - - res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone) - - self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None) - - -class TestNorwegianTimeStripper(PywikibotTestCase): - - """Test cases for Norwegian language.""" - - def setUp(self): - site = pywikibot.Site('no', 'wikipedia') - self.ts = TimeStripper(site) - super(TestNorwegianTimeStripper, self).setUp() - - def test_timestripper(self): - """Test that correct date is matched.""" - txtMatch = u'3. feb 2010 kl. 19:48 (CET) 7. feb 2010 kl. 19:48 (UTC)' - txtNoMatch = u'3 March 2010 19:48 (UTC) 7 March 2010 19:48 (UTC)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) - - res = datetime.datetime(2010, 2, 7, 19, 48, tzinfo=tzone) - - self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None) - - -class TestVietnameseTimeStripper(PywikibotTestCase): - - """Test cases for Vietnamese language.""" - - def setUp(self): - site = pywikibot.Site('vi', 'wikipedia') - self.ts = TimeStripper(site) - super(TestVietnameseTimeStripper, self).setUp() - - def test_timestripper_01(self): - """Test that correct date is matched.""" - txtMatch = u'16:41, ngày 15 tháng 9 năm 2008 (UTC) 16:41, ngày 12 tháng 9 năm 2008 (UTC)' - txtNoMatch = u'16:41, ngày 15 March 9 năm 2008 (UTC) 16:41, ngày 12 March 9 năm 2008 (UTC)' - - tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) + txtMatch = self.sites[key]['match2']
res = datetime.datetime(2008, 9, 12, 16, 41, tzinfo=tzone)
self.assertEqual(self.ts.timestripper(txtMatch), res) - self.assertEqual(self.ts.timestripper(txtNoMatch), None)
- def test_timestripper_02(self): - """Test that correct date is matched.""" - txtMatch = u'21:18, ngày 13 tháng 8 năm 2014 (UTC) 21:18, ngày 14 tháng 8 năm 2014 (UTC)' - txtNoMatch = u'21:18, ngày 13 March 8 năm 2014 (UTC) 21:18, ngày 14 March 8 năm 2014 (UTC)' + if 'match3' not in self.sites[key]: + return
- tzone = tzoneFixedOffset(self.ts.site.siteinfo['timeoffset'], - self.ts.site.siteinfo['timezone']) + txtMatch = self.sites[key]['match3']
res = datetime.datetime(2014, 8, 14, 21, 18, tzinfo=tzone)
self.assertEqual(self.ts.timestripper(txtMatch), res) + + def test_timestripper_nomatch(self, key): + """Test that correct date is not matched.""" + self.ts = TimeStripper(self.get_site(key)) + + if 'nomatch' in self.sites[key]: + txtNoMatch = self.sites[key]['nomatch'] + else: + txtNoMatch = u'3 March 2010 19:48 (UTC) 7 March 2010 19:48 (UTC)' + + self.assertEqual(self.ts.timestripper(txtNoMatch), None) + + if 'nomatch1' not in self.sites[key]: + return + + txtNoMatch = self.sites[key]['nomatch1'] self.assertEqual(self.ts.timestripper(txtNoMatch), None)
diff --git a/tests/utils.py b/tests/utils.py index 7f1bc62..3e3213a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -7,87 +7,15 @@ from __future__ import print_function __version__ = '$Id$' # -import time -import sys import pywikibot -from tests import patch_request, unpatch_request, unittest +from tests import aspects +from tests import unittest # flake8: noqa
-# Number of seconds each test may consume before a note is added after the test. -test_duration_warning_interval = 10 - - -class BaseTestCase(unittest.TestCase): - - """Base class for all test cases. - - Adds timing info to stdout. - """ - - def setUp(self): - self.test_start = time.time() - - def tearDown(self): - self.test_completed = time.time() - duration = self.test_completed - self.test_start - - if duration > test_duration_warning_interval: - print(' %0.3fs' % duration, end=' ') - sys.stdout.flush() - - -class NoSiteTestCase(BaseTestCase): - - """Test cases not connected to a Site object. - - Do not use this for mock Site objects. - - Never set a class or instance variable called 'site' - As it will prevent tests from executing when invoked as: - $ nosetests -a '!site' -v - """ - - def setUp(self): - self.old_Site_lookup_method = pywikibot.Site - pywikibot.Site = lambda *args: self.fail('%s: Site() not permitted' - % self.__class__.__name__) - - super(NoSiteTestCase, self).setUp() - - def tearDown(self): - super(NoSiteTestCase, self).tearDown() - - pywikibot.Site = self.old_Site_lookup_method - - -class SiteTestCase(BaseTestCase): - - """Test cases connected to a Site object. - - Do not use this for mock Site objects. - """ - - site = True - - -class CachedTestCase(SiteTestCase): - - """Aggressively cached API test cases. - - Patches pywikibot.data.api to aggressively cache - API responses. - """ - - def setUp(self): - patch_request() - - super(CachedTestCase, self).setUp() - - def tearDown(self): - super(CachedTestCase, self).tearDown() - - unpatch_request() - -PywikibotTestCase = CachedTestCase +BaseTestCase = aspects.TestCase +NoSiteTestCase = aspects.TestCase +SiteTestCase = aspects.TestCase +CachedTestCase = aspects.TestCase +PywikibotTestCase = aspects.TestCase
class DummySiteinfo(): diff --git a/tests/weblib_tests.py b/tests/weblib_tests.py index bbac6fe..5a00dd0 100644 --- a/tests/weblib_tests.py +++ b/tests/weblib_tests.py @@ -14,10 +14,10 @@ from urllib.parse import urlparse
import pywikibot.weblib as weblib -from tests.utils import unittest, NoSiteTestCase +from tests.aspects import unittest, TestCase
-class TestArchiveSites(NoSiteTestCase): +class TestArchiveSites(TestCase):
net = True
@@ -25,6 +25,7 @@ def setUpClass(cls): if os.environ.get('TRAVIS', 'false') == 'true': raise unittest.SkipTest('Weblib tests are disabled on Travis-CI') + super(TestArchiveSites, cls).setUpClass()
def testInternetArchiveNewest(self): archivedversion = weblib.getInternetArchiveURL('https://google.com') diff --git a/tests/wikibase_edit_tests.py b/tests/wikibase_edit_tests.py new file mode 100644 index 0000000..4a21a5d --- /dev/null +++ b/tests/wikibase_edit_tests.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +"""Tests for editing Wikibase items.""" +# +# (C) Pywikibot team, 2014 +# +# Distributed under the terms of the MIT license. +# +__version__ = '$Id$' +# + +import time +import pywikibot + +from tests.aspects import unittest, WikibaseTestCase + + +class TestWikibaseWriteGeneral(WikibaseTestCase): + + """Run general wikibase write tests.""" + + family = 'wikidata' + code = 'test' + + user = True + write = True + + def test_label_set(self): + testsite = self.get_repo() + item = pywikibot.ItemPage(testsite, 'Q68') + self.assertIsInstance(item, pywikibot.ItemPage) + self.assertEqual(item.getID(), 'Q68') + item.editLabels({'en': 'Test123'}) + item.get(force=True) + self.assertEqual(item.labels['en'], 'Test123') + + def test_label_remove(self): + testsite = self.get_repo() + item = pywikibot.ItemPage(testsite, 'Q68') + # These two should be additive + item.editLabels({'en': 'Test123'}) + item.editLabels({'fa': 'Test123'}) + item.get(force=True) + self.assertTrue('en' in item.labels.keys()) + self.assertTrue('fa' in item.labels.keys()) + + # This should remove the 'fa' label + item.editLabels({'en': 'Test123', 'fa': ''}) + + # Check 'fa' label is removed + item = pywikibot.ItemPage(testsite, 'Q68') + item.get() + self.assertFalse('fa' in item.labels.keys()) + + def test_alias_set(self): + testsite = self.get_repo() + ts = str(time.time()) + item = pywikibot.ItemPage(testsite, 'Q68') + item.editAliases({'en': [ts]}) + + def test_add_claim_with_qualifier(self): + testsite = self.get_repo() + item = pywikibot.ItemPage(testsite, 'Q68') + item.get() + if 'P115' in item.claims: + item.removeClaims(item.claims['P115']) + + claim = pywikibot.page.Claim(testsite, 'P115', datatype='wikibase-item') + target = pywikibot.ItemPage(testsite, 'Q271') + claim.setTarget(target) + + item.addClaim(claim) + + item.get(force=True) + + end_date = pywikibot.page.Claim(testsite, 'P88', isQualifier=True) + end_date.setTarget(pywikibot.WbTime(year=2012)) + item.claims['P115'][0].addQualifier(end_date) + + # metaclass cant handle this: @unittest.expectedFailure # bug 69401 + def test_edit_entity_new_item(self): + testsite = self.get_repo() + ts = str(time.time()) + data = { + 'labels': { + 'en': { + 'language': 'en', + 'value': 'Pywikibot test new item', + } + }, + 'descriptions': { + 'en': { + 'language': 'en', + 'value': 'Pywikibot test new item - ' + ts, + } + } + } + item = pywikibot.ItemPage(testsite, 'null') + item._defined_by = lambda singular=None: {} + #del item.id + item.editEntity(data) + + +if __name__ == '__main__': + try: + unittest.main() + except SystemExit: + pass diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py index 701e426..bee7313 100644 --- a/tests/wikibase_tests.py +++ b/tests/wikibase_tests.py @@ -17,12 +17,7 @@ import json import copy
-from tests.utils import PywikibotTestCase, unittest - -site = pywikibot.Site('en', 'wikipedia') -mainpage = pywikibot.Page(pywikibot.page.Link("Main Page", site)) -wikidata = site.data_repository() -wikidatatest = pywikibot.Site('test', 'wikidata').data_repository() +from tests.aspects import unittest, WikidataTestCase, TestCase
# fetch a page which is very likely to be unconnected, which doesnt have @@ -35,15 +30,19 @@ return page
-class TestGeneral(PywikibotTestCase): +class TestGeneral(WikidataTestCase): + + @classmethod + def setUpClass(cls): + super(TestGeneral, cls).setUpClass() + enwiki = pywikibot.Site('en', 'wikipedia') + cls.mainpage = pywikibot.Page(pywikibot.page.Link("Main Page", enwiki))
def testWikibase(self): - if not site.has_transcluded_data: - return - repo = site.data_repository() + repo = self.get_repo() item_namespace = repo.namespaces()[0] self.assertEqual(item_namespace.defaultcontentmodel, 'wikibase-item') - item = pywikibot.ItemPage.fromPage(mainpage) + item = pywikibot.ItemPage.fromPage(self.mainpage) self.assertIsInstance(item, pywikibot.ItemPage) self.assertEqual(item.getID(), 'Q5296') self.assertEqual(item.title(), 'Q5296') @@ -65,10 +64,10 @@ self.assertEqual(claim._formatValue(), {'entity-type': 'item', 'numeric-id': 1})
# test WbTime - t = pywikibot.WbTime(site=wikidata, year=2010, hour=12, minute=43) + t = pywikibot.WbTime(site=repo, year=2010, hour=12, minute=43) self.assertEqual(t.toTimestr(), '+00000002010-01-01T12:43:00Z') - self.assertRaises(ValueError, pywikibot.WbTime, site=wikidata, precision=15) - self.assertRaises(ValueError, pywikibot.WbTime, site=wikidata, precision='invalid_precision') + self.assertRaises(ValueError, pywikibot.WbTime, site=repo, precision=15) + self.assertRaises(ValueError, pywikibot.WbTime, site=repo, precision='invalid_precision')
# test WbQuantity q = pywikibot.WbQuantity(amount=1234, error=1) @@ -109,45 +108,17 @@ unit='invalid_unit')
# test WikibasePage.__cmp__ - self.assertEqual(pywikibot.ItemPage.fromPage(mainpage), + self.assertEqual(pywikibot.ItemPage.fromPage(self.mainpage), pywikibot.ItemPage(repo, 'q5296'))
def testItemPageExtensionability(self): class MyItemPage(pywikibot.ItemPage): pass - self.assertIsInstance(MyItemPage.fromPage(mainpage), MyItemPage) - - def test_not_supported_family(self): - """Test that family without a data repository causes error.""" - # Wikispecies is not supported by Wikidata yet. - species_site = pywikibot.Site('species', 'species') - self.wdp = pywikibot.Page(species_site, 'Main Page') - self.assertRaises(pywikibot.WikiBaseError, - pywikibot.ItemPage.fromPage, self.wdp) - self.assertRaisesRegexp(pywikibot.WikiBaseError, - 'species.*no transcluded data', - self.wdp.data_item) - - def test_own_client(self): - """Test that a data repository family can be its own client.""" - # Note: these tests do not yet verify that pywikibot can - # utilise this Wikibase configuration, as it is not yet - # working correctly on Wikidata. - - # The main Wikidata is its own client. - self.wdp = pywikibot.Page(wikidata, - wikidata.siteinfo['mainpage']) - item = pywikibot.ItemPage.fromPage(self.wdp) - self.assertEqual(item.site, self.wdp.site) - - # test.wikidata is also - self.wdp = pywikibot.Page(wikidatatest, - wikidatatest.siteinfo['mainpage']) - item = pywikibot.ItemPage.fromPage(self.wdp) - self.assertEqual(item.site, self.wdp.site) + self.assertIsInstance(MyItemPage.fromPage(self.mainpage), MyItemPage)
-class TestItemLoad(PywikibotTestCase): +class TestItemLoad(WikidataTestCase): + """Test each of the three code paths for item creation: 1. by Q id 2. ItemPage.fromPage(page) @@ -159,7 +130,27 @@ 3. missing pages to fromPage 4. unconnected pages to fromPage """ + + sites = { + 'wikidata': { + 'family': 'wikidata', + 'code': 'wikidata', + }, + 'enwiki': { + 'family': 'wikipedia', + 'code': 'en', + } + } + + @classmethod + def setUpClass(cls): + global wikidata + super(TestItemLoad, cls).setUpClass() + cls.site = cls.get_site('enwiki') + cls.nyc = pywikibot.Page(pywikibot.page.Link("New York City", cls.site)) + def test_item_normal(self): + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, 'Q60') self.assertEqual(item._link._title, 'Q60') self.assertEqual(item.id, 'Q60') @@ -174,6 +165,7 @@
def test_load_item_set_id(self): """Test setting item.id attribute on empty item.""" + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, '-1') self.assertEqual(item._link._title, '-1') item.id = 'Q60' @@ -194,6 +186,7 @@ but modifying item.id does not currently work, and this test highlights that it breaks silently. """ + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, 'Q60') item.get() self.assertEqual(item.labels['en'], 'New York City') @@ -215,9 +208,12 @@ def test_empty_item(self): # should not raise an error as the constructor only requires # the site parameter, with the title parameter defaulted to None + wikidata = self.get_repo() self.assertRaises(TypeError, pywikibot.ItemPage, wikidata)
def test_item_invalid_titles(self): + + wikidata = self.get_repo()
def check(title, exception): item = pywikibot.ItemPage(wikidata, title) @@ -239,12 +235,14 @@ check(title, APIError)
def test_item_untrimmed_title(self): + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, ' Q60 ') self.assertEqual(item._link._title, 'Q60') self.assertEqual(item.title(), 'Q60') item.get()
def test_item_missing(self): + wikidata = self.get_repo() # this item is deleted item = pywikibot.ItemPage(wikidata, 'Q404') self.assertEqual(item._link._title, 'Q404') @@ -261,7 +259,7 @@ self.assertEqual(item.exists(), False)
def test_fromPage_noprops(self): - page = pywikibot.Page(pywikibot.page.Link("New York City", site)) + page = self.nyc item = pywikibot.ItemPage.fromPage(page) self.assertEqual(item._link._title, 'Null') # not good self.assertEqual(hasattr(item, 'id'), False) @@ -275,7 +273,7 @@ self.assertEqual(item.exists(), True)
def test_fromPage_props(self): - page = pywikibot.Page(pywikibot.page.Link("New York City", site)) + page = self.nyc # fetch page properties page.properties() item = pywikibot.ItemPage.fromPage(page) @@ -293,7 +291,7 @@ self.assertEqual(item.exists(), True)
def test_fromPage_invalid_title(self): - page = pywikibot.Page(pywikibot.page.Link("[]", site)) + page = pywikibot.Page(pywikibot.page.Link("[]", self.site)) self.assertRaises(pywikibot.InvalidTitle, pywikibot.ItemPage.fromPage, page)
def _test_fromPage_noitem(self, link): @@ -341,24 +339,24 @@
def test_fromPage_redirect(self): # this is a redirect, and should not have a wikidata item - link = pywikibot.page.Link("Main page", site) + link = pywikibot.page.Link("Main page", self.site) self._test_fromPage_noitem(link)
def test_fromPage_missing(self): # this is a deleted page, and should not have a wikidata item - link = pywikibot.page.Link("Test page", site) + link = pywikibot.page.Link("Test page", self.site) self._test_fromPage_noitem(link)
def test_fromPage_noitem(self): # this is a new page, and should not have a wikidata item yet - page = _get_test_unconnected_page(site) + page = _get_test_unconnected_page(self.site) link = page._link self._test_fromPage_noitem(link)
def test_fromPage_missing_lazy(self): """Test lazy loading of item from nonexistent source page.""" # this is a deleted page, and should not have a wikidata item - link = pywikibot.page.Link("Test page", site) + link = pywikibot.page.Link("Test page", self.site) page = pywikibot.Page(link) item = pywikibot.ItemPage.fromPage(page)
@@ -371,30 +369,34 @@ self.assertRaisesRegexp(pywikibot.NoPage, 'Test page', item.get)
-class TestRedirects(PywikibotTestCase): +class TestRedirects(WikidataTestCase):
"""Test redirect and non-redirect items."""
def test_normal_item(self): + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, 'Q1') self.assertFalse(item.isRedirectPage()) self.assertRaises(pywikibot.IsNotRedirectPage, item.getRedirectTarget)
def test_redirect_item(self): + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, 'Q10008448') target = pywikibot.ItemPage(wikidata, 'Q8422626') self.assertTrue(item.isRedirectPage()) self.assertEqual(item.getRedirectTarget(), target)
-class TestPropertyPage(PywikibotTestCase): +class TestPropertyPage(WikidataTestCase):
def test_property_empty_property(self): """Test creating a PropertyPage without a title.""" + wikidata = self.get_repo() self.assertRaises(pywikibot.Error, pywikibot.PropertyPage, wikidata)
def test_globe_coordinate(self): """Test a coordinate PropertyPage has the correct type.""" + wikidata = self.get_repo() property_page = pywikibot.PropertyPage(wikidata, 'P625') self.assertEqual(property_page.type, 'globe-coordinate') self.assertEqual(property_page.getType(), 'globecoordinate') @@ -404,34 +406,38 @@ self.assertEqual(claim.getType(), 'globecoordinate')
def test_get(self): + wikidata = self.get_repo() property_page = pywikibot.PropertyPage(wikidata, 'P625') property_page.get() - self.assertEquals(property_page.type, 'globe-coordinate') + self.assertEqual(property_page.type, 'globe-coordinate')
def test_new_claim(self): """Test that PropertyPage.newClaim uses cached datatype.""" + wikidata = self.get_repo() property_page = pywikibot.PropertyPage(wikidata, 'P625') property_page.get() claim = property_page.newClaim() - self.assertEquals(claim.type, 'globe-coordinate') + self.assertEqual(claim.type, 'globe-coordinate')
# Now verify that it isnt fetching the type from the property # data in the repo by setting the cache to the incorrect type # and checking that it is the cached value that is used. property_page._type = 'wikibase-item' claim = property_page.newClaim() - self.assertEquals(claim.type, 'wikibase-item') + self.assertEqual(claim.type, 'wikibase-item')
-class TestClaimSetValue(PywikibotTestCase): +class TestClaimSetValue(WikidataTestCase):
def test_set_website(self): + wikidata = self.get_repo() claim = pywikibot.Claim(wikidata, 'P856') self.assertEqual(claim.type, 'url') claim.setTarget('https://en.wikipedia.org/') self.assertEqual(claim.target, 'https://en.wikipedia.org/')
def test_set_date(self): + wikidata = self.get_repo() claim = pywikibot.Claim(wikidata, 'P569') self.assertEqual(claim.type, 'time') claim.setTarget(pywikibot.WbTime(year=2001, month=1, day=1, site=wikidata)) @@ -440,17 +446,23 @@ self.assertEqual(claim.target.day, 1)
def test_set_incorrect_target_value(self): + wikidata = self.get_repo() claim = pywikibot.Claim(wikidata, 'P569') self.assertRaises(ValueError, claim.setTarget, 'foo') claim = pywikibot.Claim(wikidata, 'P856') self.assertRaises(ValueError, claim.setTarget, pywikibot.WbTime(2001, site=wikidata))
-class TestPageMethods(PywikibotTestCase): +class TestPageMethods(WikidataTestCase): + """Test cases to test methods of Page() behave correctly with Wikibase""" + + family = 'wikidata' + code = 'test'
def test_page_methods(self): """Test ItemPage methods inherited from superclass Page.""" + wikidatatest = self.get_repo() self.wdp = pywikibot.ItemPage(wikidatatest, 'Q6') self.assertRaises(pywikibot.PageNotSaved, self.wdp.save) self.wdp.previousRevision() @@ -460,20 +472,36 @@ self.wdp.templatesWithParams()
-class TestLinks(PywikibotTestCase): +class TestLinks(WikidataTestCase):
- """Test cases to test links stored in Wikidata.""" + """Test cases to test links stored in Wikidata. + + Uses a stored data file for the wikibase item. + However wikibase creates site objects for each sitelink, and the unit test + directly creates a Site for 'wikipedia:af' to use in a comparison. + """ + + sites = { + 'wikidata': { + 'family': 'wikidata', + 'code': 'wikidata', + }, + 'afwiki': { + 'family': 'wikipedia', + 'code': 'af', + } + }
def setUp(self): super(TestLinks, self).setUp() - self.wdp = pywikibot.ItemPage(wikidata, 'Q60') + self.wdp = pywikibot.ItemPage(self.get_repo(), 'Q60') self.wdp.id = 'Q60' self.wdp._content = json.load(open(os.path.join(os.path.split(__file__)[0], 'pages', 'Q60_only_sitelinks.wd'))) self.wdp.get()
def test_iterlinks_page_object(self): page = [pg for pg in self.wdp.iterlinks() if pg.site.language() == 'af'][0] - self.assertEqual(page, pywikibot.Page(pywikibot.Site('af', 'wikipedia'), u'New York Stad')) + self.assertEqual(page, pywikibot.Page(self.get_site('afwiki'), u'New York Stad'))
def test_iterlinks_filtering(self): wikilinks = list(self.wdp.iterlinks('wikipedia')) @@ -483,12 +511,16 @@ self.assertEqual(len(wvlinks), 2)
-class TestWriteNormalizeLang(PywikibotTestCase): +class TestWriteNormalizeLang(TestCase): + """Test cases for routines that normalize languages in a dict.
Exercises WikibasePage._normalizeLanguages with data that is not normalized and data which is already normalized. """ + + family = 'wikipedia' + lang = 'en'
def setUp(self): super(TestWriteNormalizeLang, self).setUp() @@ -507,12 +539,15 @@ self.assertEqual(response, self.lang_out)
-class TestWriteNormalizeData(PywikibotTestCase): +class TestWriteNormalizeData(TestCase): + """Test cases for routines that normalize data for writing to Wikidata.
Exercises WikibasePage._normalizeData with data that is not normalized and data which is already normalized. """ + + net = False
def setUp(self): super(TestWriteNormalizeData, self).setUp() @@ -546,12 +581,14 @@ self.assertEqual(response, self.data_out)
-class TestNamespaces(PywikibotTestCase): +class TestNamespaces(WikidataTestCase): + """Test cases to test namespaces of Wikibase entities"""
def test_empty_wikibase_page(self): # As a base class it should be able to instantiate # it with minimal arguments + wikidata = self.get_repo() page = pywikibot.page.WikibasePage(wikidata) self.assertRaises(AttributeError, page.namespace) page = pywikibot.page.WikibasePage(wikidata, title='') @@ -577,6 +614,7 @@
def test_wikibase_link_namespace(self): """Test the title resolved to a namespace correctly.""" + wikidata = self.get_repo() # title without any namespace clues (ns or entity_type) # should verify the Link namespace is appropriate page = pywikibot.page.WikibasePage(wikidata, title='Q6') @@ -586,6 +624,7 @@
def test_wikibase_namespace_selection(self): """Test various ways to correctly specify the namespace.""" + wikidata = self.get_repo() page = pywikibot.page.ItemPage(wikidata, 'Q6') self.assertEqual(page.namespace(), 0) page = pywikibot.page.ItemPage(wikidata, title='Q6') @@ -610,6 +649,7 @@
def test_wrong_namespaces(self): """Test incorrect namespaces for Wikibase entities.""" + wikidata = self.get_repo() # All subclasses of WikibasePage raise a ValueError # if the namespace for the page title is not correct self.assertRaises(ValueError, pywikibot.page.WikibasePage, wikidata, @@ -623,6 +663,7 @@ # part of the title in namespace 0 # TODO: These items have inappropriate titles, which should # raise an error. + wikidata = self.get_repo() item = pywikibot.ItemPage(wikidata, 'Invalid:Q1') self.assertEqual(item.namespace(), 0) self.assertEqual(item.id, 'INVALID:Q1') @@ -633,8 +674,11 @@ self.assertEqual(item.title(), 'INVALID:Q1')
-class TestAlternateNamespaces(PywikibotTestCase): +class TestAlternateNamespaces(TestCase): + """Test cases to test namespaces of Wikibase entities""" + + net = False
def setUp(self): super(TestAlternateNamespaces, self).setUp() @@ -693,6 +737,61 @@ self.assertEqual(prop._defined_by(), {'ids': 'P21'})
+class TestOwnClient(TestCase): + + sites = { + # The main Wikidata is its own client. + 'wikidata': { + 'family': 'wikidata', + 'code': 'wikidata', + }, + # test.wikidata is also + 'wikidatatest': { + 'family': 'wikidata', + 'code': 'test', + }, + } + + def test_own_client(self, key): + """Test that a data repository family can be its own client.""" + site = self.get_site(key) + + page = pywikibot.Page(site, 'Wikidata:Main Page') + item = pywikibot.ItemPage.fromPage(page) + self.assertEqual(item.site, site) + + +class TestUnconnectedClient(TestCase): + + """Test clients not connected to a data repository.""" + + sites = { + # Wikispecies is not supported by Wikidata yet. + 'species': { + 'family': 'species', + 'code': 'species', + 'page_title': 'Main Page', + }, + # fr.wiktionary is not supported by Wikidata yet. + 'frwikt': { + 'family': 'wiktionary', + 'code': 'fr', + 'page_title': 'and', + }, + } + + def test_not_supported_family(self, key): + """Test that family without a data repository causes error.""" + site = self.get_site(key) + + self.wdp = pywikibot.Page(site, self.sites[key]['page_title']) + self.assertRaises(pywikibot.WikiBaseError, + pywikibot.ItemPage.fromPage, self.wdp) + self.assertRaisesRegexp(pywikibot.WikiBaseError, + 'no transcluded data', + self.wdp.data_item) + + if __name__ == '__main__': try: unittest.main() diff --git a/tests/wikidataquery_tests.py b/tests/wikidataquery_tests.py index 3bfed3b..ecfe70a 100644 --- a/tests/wikidataquery_tests.py +++ b/tests/wikidataquery_tests.py @@ -10,7 +10,7 @@
import pywikibot.data.wikidataquery as query -from tests.utils import unittest, NoSiteTestCase, PywikibotTestCase +from tests.aspects import unittest, WikidataTestCase, TestCase
import pywikibot from pywikibot.page import ItemPage, PropertyPage, Claim @@ -19,11 +19,11 @@ import time
-class TestApiFunctions(PywikibotTestCase): +class TestApiFunctions(WikidataTestCase):
def setUp(self): super(TestApiFunctions, self).setUp() - self.repo = pywikibot.Site('wikidata', 'wikidata').data_repository() + self.repo = self.get_repo()
def testQueries(self): """ @@ -215,7 +215,7 @@ self.assertEqual(qs, "q=link%5Benwiki%5D&labels=en,fr&props=prop")
-class TestApiSlowFunctions(NoSiteTestCase): +class TestApiSlowFunctions(TestCase):
net = True
diff --git a/tests/xmlreader_tests.py b/tests/xmlreader_tests.py index 2f9264c..8a99c0e 100644 --- a/tests/xmlreader_tests.py +++ b/tests/xmlreader_tests.py @@ -10,10 +10,12 @@
import os.path from pywikibot import xmlreader -from tests.utils import unittest, NoSiteTestCase +from tests.aspects import unittest, TestCase
-class XmlReaderTestCase(NoSiteTestCase): +class XmlReaderTestCase(TestCase): + + net = False
@classmethod def setUpClass(cls):
pywikibot-commits@lists.wikimedia.org