123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- """
- test_collate_content.py
- =======================
- (c) 2014 - Edward J. Stronge
- Tests for the collate_content module
- """
- from collections import defaultdict, namedtuple
- import os
- import random
- import tempfile
- import shutil
- import string
- import unittest
- from pelican import Pelican
- from pelican import ArticlesGenerator, PagesGenerator
- from pelican.settings import read_settings
- import collate_content as cc
- TEMP_PAGE_TEMPLATE = """Title: {title}
- Category: {category}
- """
- Content = namedtuple('Content', ['title', 'path', 'category'])
- # Characters likely to appear in blog titles/categories. Could eventually
- # extend support to more characters that can't appear in a Python identifier
- BLOG_CHARACTERS = string.letters + ' -:'
- def make_content(directory, categories, count=5, categories_per_content=1):
- """
- make_content --> {(processed_category, original_category): articles, ...}
- Writes random titles and categories into `count` temporary
- files in `directory`. If desired, specify `categories_per_content`
- to assign multiple categories to each written file.
- Returns a dictionary whose keys are in `categories` with values
- that are (title, path, category) tuples for the generated
- content files.
- """
- new_content = defaultdict(list)
- for _ in range(count):
- title = get_random_text_and_whitespace()
- category_choice = random.sample(categories, categories_per_content)
- categories_string = ', '.join(category_choice)
- output = TEMP_PAGE_TEMPLATE.format(
- title=title, category=categories_string)
- with tempfile.NamedTemporaryFile(
- dir=directory, mode='w', suffix='.md', delete=False) as tmp:
- tmp.write(output)
- path = os.path.join(directory, tmp.name)
- for each_cat in category_choice:
- new_content[(cc.substitute_category_name(each_cat), each_cat)]\
- .append(Content(title, path, categories_string))
- return new_content
- def get_random_text_and_whitespace(length=10):
- """
- Returns at most `length` randomly-generated letters and/or
- whitespace. The returned string will not begin or end in whitespace.
- """
- return "".join(random.sample(BLOG_CHARACTERS, length)).strip()
- def modified_pelican_run(self):
- """Runs the generators and returns the context object
- Modified from the Pelican object's run methods.
- """
- context = self.settings.copy()
- context['filenames'] = {} # share the dict between all the generators
- context['localsiteurl'] = self.settings['SITEURL'] # share
- generators = [
- cls(
- context=context,
- settings=self.settings,
- path=self.path,
- theme=self.theme,
- output_path=self.output_path,
- ) for cls in self.get_generator_classes()
- ]
- for p in generators:
- if hasattr(p, 'generate_context'):
- p.generate_context()
- writer = self.get_writer()
- for p in generators:
- if hasattr(p, 'generate_output'):
- p.generate_output(writer)
- next(g for g in generators if isinstance(g, ArticlesGenerator))
- next(g for g in generators if isinstance(g, PagesGenerator))
- return context
- class ContentCollationTester(unittest.TestCase):
- """Test generation of lists of content based on their Category metadata"""
- def setUp(self, settings_overrides=None, count=5,
- categories_per_content=1, categories=None):
- self.temp_input_dir = tempfile.mkdtemp(prefix="cc-input-")
- page_directory = os.path.join(self.temp_input_dir, 'pages')
- os.mkdir(page_directory)
- self.temp_output_dir = tempfile.mkdtemp(prefix="cc-output-")
- if categories is None:
- categories = [get_random_text_and_whitespace() for _ in range(5)]
- self.articles = make_content(
- self.temp_input_dir, categories, count=count,
- categories_per_content=categories_per_content)
- self.pages = make_content(
- page_directory, categories, count=count,
- categories_per_content=categories_per_content)
- settings = {
- 'PATH': self.temp_input_dir,
- 'PAGE_DIR': 'pages',
- 'OUTPUT_PATH': self.temp_output_dir,
- 'PLUGINS': [cc],
- 'DEFAULT_DATE': (2014, 6, 8),
- }
- if settings_overrides is not None:
- settings.update(settings_overrides)
- settings = read_settings(override=settings)
- pelican = Pelican(settings=settings)
- pelican.modified_run = modified_pelican_run
- self.collations = pelican.modified_run(pelican)['collations']
- def tearDown(self):
- shutil.rmtree(self.temp_input_dir)
- shutil.rmtree(self.temp_output_dir)
- class TestCollation(ContentCollationTester):
- """Test generation of lists of content based on their Category metadata"""
- def test_articles_with_one_category(self):
- for substituted_category, original_category in self.articles.keys():
- collation_key = '%s_articles' % substituted_category
- self.assertIn(collation_key, self.collations)
- collated_titles = [a.title for a in self.collations[collation_key]]
- for title, _, _ in self.articles[
- (substituted_category, original_category)]:
- self.assertIn(title, collated_titles)
- def test_pages_with_one_category(self):
- for substituted_category, original_category in self.pages.keys():
- collation_key = '%s_pages' % substituted_category
- self.assertIn(collation_key, self.collations)
- collated_titles = [a.title for a in self.collations[collation_key]]
- for title, _, _ in self.pages[
- (substituted_category, original_category)]:
- self.assertIn(title, collated_titles)
- class TestCollationAndMultipleCategories(TestCollation):
- """
- Test collate_content with multiple categories specified in each
- article and each page.
- """
- def setUp(self):
- categories = [get_random_text_and_whitespace() for _ in range(5)]
- ContentCollationTester.setUp(
- self, categories=categories, categories_per_content=3)
- class TestFilteredCategories(ContentCollationTester):
- """
- Test collate_content with the `CATEGORIES_TO_COLLATE` setting
- in effect
- """
- def setUp(self):
- categories = [get_random_text_and_whitespace() for _ in range(5)]
- self.retained_categories = categories[:2]
- override = {'CATEGORIES_TO_COLLATE': self.retained_categories}
- ContentCollationTester.setUp(
- self, settings_overrides=override, categories=categories)
- def test_articles_with_one_category_after_filtering(self):
- for substituted_category, original_category in self.articles.keys():
- collation_key = '%s_articles' % substituted_category
- if original_category not in self.retained_categories:
- self.assertNotIn(collation_key, self.collations)
- continue
- self.assertIn(collation_key, self.collations)
- collated_titles = [a.title for a in self.collations[collation_key]]
- for title, _, _ in self.articles[
- (substituted_category, original_category)]:
- self.assertIn(title, collated_titles)
- def test_pages_with_one_category_after_filtering(self):
- for substituted_category, original_category in self.pages.keys():
- collation_key = '%s_pages' % substituted_category
- if original_category not in self.retained_categories:
- self.assertNotIn(collation_key, self.collations)
- continue
- self.assertIn(collation_key, self.collations)
- collated_titles = [a.title for a in self.collations[collation_key]]
- for title, _, _ in self.pages[
- (substituted_category, original_category)]:
- self.assertIn(title, collated_titles)
- class TestFilteredAndMultipleCategories(TestFilteredCategories):
- """
- Test collate_content with the `CATEGORIES_TO_COLLATE` setting
- in effect as well as with multiple categories specified in each
- article and each page.
- """
- def setUp(self):
- categories = [get_random_text_and_whitespace() for _ in range(5)]
- self.retained_categories = categories[:2]
- override = {'CATEGORIES_TO_COLLATE': self.retained_categories}
- ContentCollationTester.setUp(
- self, settings_overrides=override, categories=categories,
- categories_per_content=3)
- if __name__ == '__main__':
- suite = unittest.TestLoader().loadTestsFromNames(['test_collate_content'])
- unittest.TextTestRunner(verbosity=1).run(suite)
|