Revision: 424ab1283ab3
Author: Robot Framework Developers ([email protected])
Date: Wed Nov 9 05:50:28 2011
Log: moved statistics to model package, use model objects instead of
mocks in unit tests
http://code.google.com/p/robotframework/source/detail?r=424ab1283ab3
Added:
/src/robot/model/statistics.py
/utest/model/test_statistics.py
=======================================
--- /dev/null
+++ /src/robot/model/statistics.py Wed Nov 9 05:50:28 2011
@@ -0,0 +1,332 @@
+# Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+
+from robot import utils
+
+
+class Statistics:
+
+ def __init__(self, suite, suite_stat_level=-1, tag_stat_include=None,
+ tag_stat_exclude=None, tag_stat_combine=None,
tag_doc=None,
+ tag_stat_link=None):
+ self.tags = TagStatistics(tag_stat_include, tag_stat_exclude,
+ tag_stat_combine, tag_doc, tag_stat_link)
+ self.suite = SuiteStatistics(suite, self.tags, suite_stat_level)
+ self.total = TotalStatistics(self.suite)
+ self.tags.sort()
+
+ #TODO: Replace with visit
+ def serialize(self, serializer):
+ serializer.start_statistics(self)
+ self.total.serialize(serializer)
+ self.tags.serialize(serializer)
+ self.suite.serialize(serializer)
+ serializer.end_statistics(self)
+
+ def visit(self, visitor):
+ self.serialize(visitor)
+
+
+class Stat:
+
+ def __init__(self, name=''):
+ self.name = name
+ self.passed = 0
+ self.failed = 0
+
+ @property
+ def total(self):
+ return self.passed + self.failed
+
+ def add_stat(self, other):
+ self.passed += other.passed
+ self.failed += other.failed
+
+ def add_test(self, test):
+ if test.status == 'PASS':
+ self.passed += 1
+ else:
+ self.failed += 1
+
+ def fail_all(self):
+ self.failed += self.passed
+ self.passed = 0
+
+ def add_suite(self, suite):
+ for test in suite.tests:
+ if self._is_included(test):
+ self.add_test(test)
+ for suite in suite.suites:
+ self.add_stat(self._subsuite_stats(suite))
+
+ def _is_included(self, test):
+ return True
+
+ def _subsuite_stats(self, suite):
+ return suite.all_stats
+
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
+
+ def __nonzero__(self):
+ return self.failed == 0
+
+
+class CriticalStats(Stat):
+
+ def __init__(self, suite):
+ Stat.__init__(self)
+ self.add_suite(suite)
+
+ def _is_included(self, test):
+ return test.critical == 'yes'
+
+ def _subsuite_stats(self, suite):
+ return suite.critical_stats
+
+class AllStats(Stat):
+
+ def __init__(self, suite):
+ Stat.__init__(self)
+ self.add_suite(suite)
+
+
+class SuiteStat(Stat):
+ type = 'suite'
+
+ def __init__(self, suite):
+ Stat.__init__(self, suite.name)
+ self.longname = suite.longname
+ self.id = suite.id
+
+ def serialize(self, serializer):
+ serializer.suite_stat(self)
+
+
+class TagStat(Stat):
+ type = 'tag'
+
+ def __init__(self, name, doc='', links=[], critical=False,
+ non_critical=False, combined=''):
+ Stat.__init__(self, name)
+ self.doc = doc
+ self.links = links
+ self.critical = critical
+ self.non_critical = non_critical
+ self.combined = combined
+ self.tests = []
+
+ def add_test(self, test):
+ Stat.add_test(self, test)
+ self.tests.append(test)
+
+ def __cmp__(self, other):
+ if self.critical != other.critical:
+ return cmp(other.critical, self.critical)
+ if self.non_critical != other.non_critical:
+ return cmp(other.non_critical, self.non_critical)
+ if bool(self.combined) != bool(other.combined):
+ return cmp(bool(other.combined), bool(self.combined))
+ return cmp(self.name, other.name)
+
+ def serialize(self, serializer):
+ serializer.tag_stat(self)
+
+
+class TotalStat(Stat):
+ type = 'total'
+
+ def __init__(self, name, suite_stat):
+ Stat.__init__(self, name)
+ self.passed = suite_stat.passed
+ self.failed = suite_stat.failed
+
+ def serialize(self, serializer):
+ serializer.total_stat(self)
+
+
+class SuiteStatistics:
+
+ def __init__(self, suite, tag_stats, suite_stat_level=-1):
+ self.all = SuiteStat(suite)
+ self.critical = SuiteStat(suite)
+ self.suites = []
+ self._process_suites(suite, tag_stats)
+ self._process_tests(suite, tag_stats)
+ self._suite_stat_level = suite_stat_level
+
+ def _process_suites(self, suite, tag_stats):
+ for subsuite in suite.suites:
+ substat = SuiteStatistics(subsuite, tag_stats)
+ self.suites.append(substat)
+ self.all.add_stat(substat.all)
+ self.critical.add_stat(substat.critical)
+
+ def _process_tests(self, suite, tag_stats):
+ for test in suite.tests:
+ self.all.add_test(test)
+ if test.critical == 'yes':
+ self.critical.add_test(test)
+ tag_stats.add_test(test, suite.critical)
+
+ def serialize(self, serializer):
+ serializer.start_suite_stats(self)
+ self._serialize(serializer, self._suite_stat_level)
+ serializer.end_suite_stats(self)
+
+ def _serialize(self, serializer, max_suite_level, suite_level=1):
+ self.all.serialize(serializer)
+ if max_suite_level < 0 or max_suite_level > suite_level:
+ for suite in self.suites:
+ suite._serialize(serializer, max_suite_level,
suite_level+1)
+
+
+class TagStatistics:
+
+ def __init__(self, include=None, exclude=None, combine=None, docs=None,
+ links=None):
+ self.stats = utils.NormalizedDict(ignore=['_'])
+ self._include = include or []
+ self._exclude = exclude or []
+ self._combine = combine or []
+ info = TagStatInfo(docs or [], links or [])
+ self._get_doc = info.get_doc
+ self._get_links = info.get_links
+
+ def add_test(self, test, critical):
+ self._add_tags_statistics(test, critical)
+ self._add_combined_statistics(test)
+
+ def _add_tags_statistics(self, test, critical):
+ for tag in test.tags:
+ if not self._is_included(tag):
+ continue
+ if tag not in self.stats:
+ self.stats[tag] = TagStat(tag, self._get_doc(tag),
+ self._get_links(tag),
+ critical.is_critical(tag),
+ critical.is_non_critical(tag))
+ self.stats[tag].add_test(test)
+
+ def _is_included(self, tag):
+ if self._include and not utils.matches_any(tag, self._include):
+ return False
+ return not utils.matches_any(tag, self._exclude)
+
+ def _add_combined_statistics(self, test):
+ for pattern, name in self._combine:
+ name = name or pattern
+ if name not in self.stats:
+ self.stats[name] = TagStat(name, self._get_doc(name),
+ self._get_links(name),
+ combined=pattern)
+ if test.is_included([pattern], []):
+ self.stats[name].add_test(test)
+
+ def serialize(self, serializer):
+ serializer.start_tag_stats(self)
+ for stat in sorted(self.stats.values()):
+ stat.serialize(serializer)
+ serializer.end_tag_stats(self)
+
+ def sort(self):
+ for stat in self.stats.values():
+ stat.tests.sort()
+
+
+class TotalStatistics:
+
+ def __init__(self, suite):
+ self.critical = TotalStat('Critical Tests', suite.critical)
+ self.all = TotalStat('All Tests', suite.all)
+
+ def serialize(self, serializer):
+ serializer.start_total_stats(self)
+ self.critical.serialize(serializer)
+ self.all.serialize(serializer)
+ serializer.end_total_stats(self)
+
+
+class TagStatInfo:
+
+ def __init__(self, docs, links):
+ self._docs = [TagStatDoc(*doc) for doc in docs]
+ self._links = [TagStatLink(*link) for link in links]
+
+ def get_doc(self, tag):
+ return ' & '.join(doc.text for doc in self._docs if
doc.matches(tag))
+
+ def get_links(self, tag):
+ return [link.get_link(tag) for link in self._links if
link.matches(tag)]
+
+
+class TagStatDoc:
+
+ def __init__(self, pattern, doc):
+ self.text = doc
+ self._pattern = pattern
+
+ def matches(self, tag):
+ return utils.matches(tag, self._pattern)
+
+
+class TagStatLink:
+ _match_pattern_tokenizer = re.compile('(\*|\?)')
+
+ def __init__(self, pattern, link, title):
+ self._regexp = self._get_match_regexp(pattern)
+ self._link = link
+ self._title = title.replace('_', ' ')
+
+ def matches(self, tag):
+ return self._regexp.match(tag) is not None
+
+ def get_link(self, tag):
+ match = self._regexp.match(tag)
+ if not match:
+ return None
+ link, title = self._replace_groups(self._link, self._title, match)
+ return link, title
+
+ def _replace_groups(self, link, title, match):
+ for index, group in enumerate(match.groups()):
+ placefolder = '%' + str(index+1)
+ link = link.replace(placefolder, group)
+ title = title.replace(placefolder, group)
+ return link, title
+
+ def _get_match_regexp(self, pattern):
+ regexp = []
+ open_parenthesis = False
+ for token in self._match_pattern_tokenizer.split(pattern):
+ if token == '':
+ continue
+ if token == '?':
+ if not open_parenthesis:
+ regexp.append('(')
+ open_parenthesis = True
+ regexp.append('.')
+ continue
+ if open_parenthesis:
+ regexp.append(')')
+ open_parenthesis = False
+ if token == '*':
+ regexp.append('(.*)')
+ continue
+ regexp.append(re.escape(token))
+ if open_parenthesis:
+ regexp.append(')')
+ return re.compile('^%s$' % ''.join(regexp), re.IGNORECASE)
=======================================
--- /dev/null
+++ /utest/model/test_statistics.py Wed Nov 9 05:50:28 2011
@@ -0,0 +1,352 @@
+import unittest
+from robot.model.critical import Critical
+
+from robot.utils.asserts import *
+from robot.model.statistics import *
+from robot.result.model import TestSuite, TestCase
+
+
+def verify_stat(stat, name, passed, failed, critical=None, non_crit=None,
id=None):
+ assert_equals(stat.name, name, 'stat.name')
+ assert_equals(stat.passed, passed)
+ assert_equals(stat.failed, failed)
+ if critical is not None:
+ assert_equals(stat.critical, critical)
+ if non_crit is not None:
+ assert_equals(stat.non_critical, non_crit)
+ if id:
+ assert_equal(stat.id, id)
+
+def verify_suite(suite, name, id, crit_pass, crit_fail, all_pass=None,
all_fail=None):
+ verify_stat(suite.critical, name, crit_pass, crit_fail, id=id)
+ if all_pass is None:
+ all_pass, all_fail = crit_pass, crit_fail
+ verify_stat(suite.all, name, all_pass, all_fail, id=id)
+
+def generate_default_suite():
+ suite = TestSuite(name='Root Suite')
+ suite.set_criticality(critical_tags=['smoke'])
+ s1 = suite.suites.create(name='First Sub Suite')
+ s2 = suite.suites.create(name='Second Sub Suite')
+ s11 = s1.suites.create(name='Sub Suite 1_1')
+ s12 = s1.suites.create(name='Sub Suite 1_2')
+ s13 = s1.suites.create(name='Sub Suite 1_3')
+ s21 = s2.suites.create(name='Sub Suite 2_1')
+ s11.tests = [TestCase(status='PASS'), TestCase(status='FAIL',
tags=['t1'])]
+ s12.tests = [TestCase(status='PASS', tags=['t_1','t2',]),
+ TestCase(status='PASS', tags=['t1','smoke']),
+ TestCase(status='FAIL', tags=['t1','t2','t3','smoke'])]
+ s13.tests = [TestCase(status='PASS', tags=['t1','t 2','smoke'])]
+ s21.tests = [TestCase(status='FAIL', tags=['t3','Smoke'])]
+ return suite
+
+
+class TestStatisticsSimple(unittest.TestCase):
+
+ def setUp(self):
+ suite = TestSuite(name='Hello')
+ suite.tests = [TestCase(status='PASS'), TestCase(status='PASS'),
+ TestCase(status='FAIL')]
+ self.statistics = Statistics(suite)
+
+ def test_total(self):
+ verify_stat(self.statistics.total.critical, 'Critical Tests', 2, 1)
+ verify_stat(self.statistics.total.all, 'All Tests', 2, 1)
+
+ def test_suite(self):
+ verify_suite(self.statistics.suite, 'Hello', 's1', 2, 1)
+
+ def test_tags(self):
+ assert_equals(self.statistics.tags.stats, {})
+
+
+class TestStatisticsNotSoSimple(unittest.TestCase):
+
+ def setUp(self):
+ self.statistics = Statistics(generate_default_suite())
+
+ def test_total(self):
+ verify_stat(self.statistics.total.all, 'All Tests', 4, 3)
+ verify_stat(self.statistics.total.critical, 'Critical Tests', 2, 2)
+
+ def test_suite(self):
+ suite = self.statistics.suite
+ verify_suite(suite, 'Root Suite', 's1', 2, 2, 4, 3)
+ assert_equals(len(suite.suites), 2)
+ s1, s2 = suite.suites
+ verify_suite(s1, 'First Sub Suite', 's1-s1', 2, 1, 4, 2)
+ verify_suite(s2, 'Second Sub Suite', 's1-s2', 0, 1, 0, 1)
+ assert_equals(len(s1.suites), 3)
+ s11, s12, s13 = s1.suites
+ verify_suite(s11, 'Sub Suite 1_1', 's1-s1-s1', 0, 0, 1, 1)
+ verify_suite(s12, 'Sub Suite 1_2', 's1-s1-s2', 1, 1, 2, 1)
+ verify_suite(s13, 'Sub Suite 1_3', 's1-s1-s3', 1, 0, 1, 0)
+ assert_equals(len(s2.suites), 1)
+ s21 = s2.suites[0]
+ verify_suite(s21, 'Sub Suite 2_1', 's1-s2-s1', 0, 1, 0, 1)
+
+ def test_tags(self):
+ tags = self.statistics.tags
+ keys = tags.stats.keys()
+ assert_equals(len(keys), 4)
+ keys.sort()
+ assert_equals(keys, 'smoke t1 t2 t3'.split())
+ verify_stat(tags.stats['smoke'], 'smoke', 2, 2, True, False)
+ verify_stat(tags.stats['t1'], 't1', 3, 2, False, False)
+ verify_stat(tags.stats['t2'], 't2', 2, 1, False, False)
+ verify_stat(tags.stats['t3'], 't3', 0, 2, False, False)
+
+
+_incl_excl_data = [
+ ([], []),
+ ([], ['t1','t2']),
+ (['t1'], ['t1','t2']),
+ (['t1','t2'], ['t1','t2','t3','t4']),
+ (['UP'], ['t1','t2','up']),
+ (['not','not2'], ['t1','t2','t3']),
+ (['t*'], ['t1','s1','t2','t3','s2','s3']),
+ (['T*','r'], ['t1','t2','r','teeeeeeee']),
+ (['*'], ['t1','t2','s1','tag']),
+ (['t1','t2','t3','not'], ['t1','t2','t3','t4','s1','s2'])
+]
+
+
+class TestTagStatistics(unittest.TestCase):
+
+ def test_include(self):
+ for incl, tags in _incl_excl_data:
+ tagstats = TagStatistics(incl, [])
+ tagstats.add_test(TestCase(status='PASS', tags=tags),
Critical())
+ exp_keys = [tag for tag in sorted(tags)
+ if incl == [] or utils.matches_any(tag, incl)]
+ assert_equal(sorted(tagstats.stats.keys()),
+ exp_keys, "Incls: %s " % incl)
+
+ def test_exclude(self):
+ for excl, tags in _incl_excl_data:
+ tagstats = TagStatistics([], excl)
+ tagstats.add_test(TestCase(status='PASS', tags=tags),
Critical())
+ exp_keys = [tag for tag in sorted(tags)
+ if not utils.matches_any(tag, excl)]
+ assert_equal(sorted(tagstats.stats.keys()),
+ exp_keys, "Excls: %s" % excl)
+
+ def test_include_and_exclude(self):
+ for incl, excl, tags, exp in [
+ ([], [], ['t0','t1','t2'], ['t0','t1','t2']),
+ (['t1'], ['t2'], ['t0','t1','t2'], ['t1']),
+ (['t?'], ['t2'], ['t0','t1','t2','x'], ['t0','t1']),
+ (['t?'], ['*2'], ['t0','t1','t2','x2'], ['t0','t1']),
+ (['t1','t2'], ['t2'], ['t0','t1','t2'], ['t1']),
+ (['t1','t2','t3','not'], ['t2','t0'],
+ ['t0','t1','t2','t3','x'], ['t1','t3'] )
+ ]:
+ tagstats = TagStatistics(incl, excl)
+ tagstats.add_test(TestCase(status='PASS', tags=tags),
Critical())
+ assert_equal(sorted(tagstats.stats.keys()),
+ exp, "Incls: %s, Excls: %s" % (incl, excl))
+
+ def test_combine_with_name(self):
+ for comb_tags, expected_name in [
+ ([], '' ),
+ ([('t1&t2', 'my name')], 'my name'),
+ ([('t1NOTt3', 'Others')], 'Others'),
+ ([('1:2&2:3', 'nAme')], 'nAme'),
+ ([('3*', '')], '3*' ),
+ ([('4NOT5', 'Some new name')], 'Some new name')
+ ]:
+ stats = TagStatistics(combine=comb_tags)
+ test = TestCase()
+ stats._add_combined_statistics(test)
+ assert_equals(len(stats.stats), expected_name != '')
+ if expected_name:
+ assert_equals(stats.stats[expected_name].name,
expected_name)
+
+ def test_is_combined_with_and_statements(self):
+ for comb_tags, test_tags, expected_count in [
+ ('t1', ['t1'], 1),
+ ('t1', ['t2'], 0),
+ ('t1&t2', ['t1'], 0),
+ ('t1&t2', ['t1','t2'], 1),
+ ('t1&t2', ['T1','t 2','t3'], 1),
+ ('t*', ['s','t','u'], 1),
+ ('t*', ['s','tee','t'], 1),
+ ('t*&s', ['s','tee','t'], 1),
+ ('t*&s&non', ['s','tee','t'], 0)
+ ]:
+ self._test_combined_statistics(comb_tags, test_tags,
expected_count)
+
+ def _test_combined_statistics(self, comb_tags, test_tags,
expected_count):
+ stats = TagStatistics(combine=[(comb_tags, 'name')])
+ test = TestCase(tags=test_tags)
+ stats._add_combined_statistics(test)
+ assert_equals(len(stats.stats['Name'].tests), expected_count,
+ 'comb: %s, test: %s' % (comb_tags, test_tags))
+
+ def test_is_combined_with_not_statements(self):
+ stats = TagStatistics()
+ for comb_tags, test_tags, expected_count in [
+ ('t1NOTt2', [], 0),
+ ('t1NOTt2', ['t1'], 1),
+ ('t1NOTt2', ['t1','t2'], 0),
+ ('t1NOTt2', ['t3'], 0),
+ ('t1NOTt2', ['t3','t2'], 0),
+ ('t*NOTt2', ['t1'], 1),
+ ('t*NOTt2', ['t'], 1),
+ ('t*NOTt2', ['TEE'], 1),
+ ('t*NOTt2', ['T2'], 0),
+ ('T*NOTT?', ['t'], 1),
+ ('T*NOTT?', ['tt'], 0),
+ ('T*NOTT?', ['ttt'], 1),
+ ('T*NOTT?', ['tt','t'], 0),
+ ('T*NOTT?', ['ttt','something'], 1),
+ ('tNOTs*NOTr', ['t'], 1),
+ ('tNOTs*NOTr', ['t','s'], 0),
+ ('tNOTs*NOTr', ['S','T'], 0),
+ ('tNOTs*NOTr', ['R','T','s'], 1),
+ ]:
+ self._test_combined_statistics(comb_tags, test_tags,
expected_count)
+
+ def test_combine(self):
+ # This is more like an acceptance test than a unit test ...
+ for comb_tags, comb_matches, tests_tags, crit_tags in [
+ (['t1&t2'], [1], [['t1','t2','t3'],['t1','t3']], []),
+ (['1&2&3'], [2], [['1','2','3'],['1','2','3','4']],
['1','2']),
+ (['1&2','1&3'], [1,2], [['1','2','3'],['1','3'],['1']],
['1']),
+ (['t*'], [3], [['t1','x','y'],['tee','z'],['t']], ['x']),
+ (['t?&s'], [2],
[['t1','s'],['tt','s','u'],['tee','s'],['s']], []),
+ (['t*&s','*'], [2,3],
[['s','t','u'],['tee','s'],[],['x']], []),
+ (['tNOTs'], [1], [['t','u'],['t','s']], []),
+ (['tNOTs','t&s','tNOTsNOTu', 't&sNOTu'], [3,2,4,1],
+ [['t','u'],['t','s'],['s','t','u'],['t'],['t','v']],
['t']),
+ (['nonex'], [0], [['t1'],['t1,t2'],[]], [])
+ ]:
+ # 1) Create tag stats
+ tagstats = TagStatistics(combine=[(t, '') for t in comb_tags])
+ all_tags = []
+ for tags in tests_tags:
+ tagstats.add_test(TestCase(status='PASS', tags=tags),
+ Critical(crit_tags))
+ all_tags.extend(tags)
+ # 2) Actual values
+ names = [stat.name for stat in sorted(tagstats.stats.values())]
+ # 3) Expected values
+ exp_crit = []; exp_noncr = []; exp_comb = []
+ for tag in utils.normalize_tags(all_tags):
+ if tag in crit_tags:
+ exp_crit.append(tag)
+ else:
+ exp_noncr.append(tag)
+ for comb, count in zip(comb_tags, comb_matches):
+ exp_comb.append(comb)
+ try:
+ assert_equals(len(tagstats.stats[comb].tests), count,
comb)
+ except KeyError:
+ fail("No key %s. Stats: %s" % (comb, tagstats.stats))
+ exp_names = exp_crit + sorted(exp_comb) + exp_noncr
+ # 4) Verify names (match counts were already verified)
+ assert_equals(names, exp_names)
+
+ def test_through_suite(self):
+ suite = generate_default_suite()
+ suite.set_criticality(critical_tags=['smoke'])
+ statistics = Statistics(suite, -1, ['t*','smoke'], ['t3'],
+ [('t1 & t2', ''), ('t? & smoke', ''),
+ ('t1 NOT t2', ''), ('none & t1', 'a
title')])
+ stats = sorted(statistics.tags.stats.values())
+ expected = [('smoke', 4), ('a title', 0), ('t1 & t2', 3),
+ ('t1 NOT t2', 2), ('t? & smoke', 4), ('t1', 5), ('t2',
3)]
+ names = [stat.name for stat in stats]
+ exp_names = [name for name, _ in expected]
+ assert_equals(names, exp_names)
+ for name, count in expected:
+ assert_equals(len(statistics.tags.stats[name].tests), count,
name)
+
+
+class TestTagStatLink(unittest.TestCase):
+
+ def test_valid_string_is_parsed_correctly(self):
+ for arg, exp in [(('Tag', 'bar/foo.html', 'foobar'),
+ ('^Tag$', 'bar/foo.html', 'foobar')),
+ (('hello', 'gopher://hello.world:8090/hello.html',
+ 'Hello World'),
+
('^hello$', 'gopher://hello.world:8090/hello.html',
+ 'Hello World'))]:
+ link = TagStatLink(*arg)
+ assert_equal(exp[0], link._regexp.pattern)
+ assert_equal(exp[1], link._link)
+ assert_equal(exp[2], link._title)
+
+ def test_valid_string_containing_patterns_is_parsed_correctly(self):
+ for arg, exp_pattern in [('*', '^(.*)$'), ('f*r', '^f(.*)r$'),
+ ('*a*', '^(.*)a(.*)$'), ('?', '^(.)$'),
+ ('??', '^(..)$'),
('f???ar', '^f(...)ar$'),
+ ('F*B?R*?', '^F(.*)B(.)R(.*)(.)$')]:
+ link = TagStatLink(arg, 'some_url', 'some_title')
+ assert_equal(exp_pattern, link._regexp.pattern)
+
+ def test_underscores_in_title_are_converted_to_spaces(self):
+ link = TagStatLink('', '', 'my_name')
+ assert_equal(link._title, 'my name')
+
+ def test_get_link_returns_correct_link_when_matches(self):
+ for arg, exp in [(('smoke', 'http://tobacco.com', 'Lung_cancer'),
+ ('http://tobacco.com', 'Lung cancer')),
+ (('tag', 'ftp://foo:809/bar.zap', 'Foo_in a Bar'),
+ ('ftp://foo:809/bar.zap', 'Foo in a Bar'))]:
+ link = TagStatLink(*arg)
+ assert_equals(exp, link.get_link(arg[0]))
+
+ def test_get_link_returns_none_when_no_match(self):
+ link = TagStatLink('smoke', 'http://tobacco.com', 'Lung cancer')
+ for tag in ['foo', 'b a r', 's moke']:
+ assert_none(link.get_link(tag))
+
+ def test_pattern_matches_case_insensitively(self):
+ exp = 'http://tobacco.com', 'Lung cancer'
+ link = TagStatLink('smoke', *exp)
+ for tag in ['Smoke', 'SMOKE', 'smoke']:
+ assert_equals(exp, link.get_link(tag))
+
+ def test_pattern_matches_when_spaces(self):
+ exp = 'http://tobacco.com', 'Lung cancer'
+ link = TagStatLink('smoking kills', *exp)
+ for tag in ['Smoking Kills', 'SMOKING KILLS']:
+ assert_equals(exp, link.get_link(tag))
+
+ def test_pattern_match(self):
+ link = TagStatLink('f?o*r', 'http://foo/bar.html', 'FooBar')
+ for tag in ['foobar', 'foor', 'f_ofoobarfoobar', 'fOoBAr']:
+ assert_equal(link.get_link(tag),
('http://foo/bar.html', 'FooBar'))
+
+ def test_pattern_substitution_with_one_match(self):
+ link = TagStatLink('tag-*', 'http://tracker/?id=%1', 'Tracker')
+ for id in ['1', '23', '456']:
+ exp = ('http://tracker/?id=%s' % id, 'Tracker')
+ assert_equal(exp, link.get_link('tag-%s' % id))
+
+ def test_pattern_substitution_with_multiple_matches(self):
+ link = TagStatLink('?-*', 'http://tracker/?id=%1-%2', 'Tracker')
+ for id1, id2 in [('1', '2'), ('3', '45'), ('f', 'bar')]:
+ exp = ('http://tracker/?id=%s-%s' % (id1, id2), 'Tracker')
+ assert_equal(exp, link.get_link('%s-%s' % (id1, id2)))
+
+ def test_pattern_substitution_with_multiple_substitutions(self):
+ link = TagStatLink('?-?-*', '%3-%3-%1-%2-%3', 'Tracker')
+ assert_equal(link.get_link('a-b-XXX'),
('XXX-XXX-a-b-XXX', 'Tracker'))
+
+ def test_matches_are_ignored_in_pattern_substitution(self):
+ link = TagStatLink('?-*-*-?', '%4-%2-%2-%4', 'Tracker')
+ assert_equal(link.get_link('A-XXX-ABC-B'),
('B-XXX-XXX-B', 'Tracker'))
+
+
+class TestTagStatLinks(unittest.TestCase):
+
+ def test_tag_stat_links_with_valid_tags(self):
+ values = [('1', '2', '3'), ('tag', 'foo.html', 'bar')]
+ tag_stat_links = TagStatInfo([], values)
+ assert_equal(len(tag_stat_links._links), 2)
+
+
+if __name__ == "__main__":
+ unittest.main()