| @@ -6,8 +6,7 @@ from hyde.fs import File, Folder | |||
| from hyde.template import Template | |||
| from jinja2 import contextfunction, Environment, FileSystemLoader, Undefined | |||
| class LoyalUndefined(Undefined): | |||
| class SilentUndefined(Undefined): | |||
| def __getattr__(self, name): | |||
| return self | |||
| @@ -45,12 +44,11 @@ class Jinja2Template(Template): | |||
| if config: | |||
| loader = FileSystemLoader([ | |||
| str(config.content_root_path), | |||
| str(config.media_root_path), | |||
| str(config.layout_root_path), | |||
| ]) | |||
| else: | |||
| loader = FileSystemLoader(str(self.sitepath)) | |||
| self.env = Environment(loader=loader, undefined=LoyalUndefined) | |||
| self.env = Environment(loader=loader, undefined=SilentUndefined) | |||
| self.env.globals['media_url'] = media_url | |||
| self.env.globals['content_url'] = content_url | |||
| @@ -171,12 +171,27 @@ class File(FS): | |||
| (mime, encoding) = mimetypes.guess_type(self.path) | |||
| return mime | |||
| @property | |||
| def is_binary(self): | |||
| """Return true if this is a binary file.""" | |||
| with open(self.path, 'rb') as fin: | |||
| CHUNKSIZE = 1024 | |||
| while 1: | |||
| chunk = fin.read(CHUNKSIZE) | |||
| if '\0' in chunk: | |||
| return True | |||
| if len(chunk) < CHUNKSIZE: | |||
| break | |||
| return False | |||
| @property | |||
| def is_text(self): | |||
| return self.mimetype.split("/")[0] == "text" | |||
| """Return true if this is a text file.""" | |||
| return (not self.is_binary) | |||
| @property | |||
| def is_image(self): | |||
| """Return true if this is an image file.""" | |||
| return self.mimetype.split("/")[0] == "image" | |||
| def read_all(self, encoding='utf-8'): | |||
| @@ -3,6 +3,7 @@ The generator class and related utility functions. | |||
| """ | |||
| from hyde.exceptions import HydeException | |||
| from hyde.fs import File | |||
| from hyde.plugin import Plugin | |||
| from hyde.template import Template | |||
| from contextlib import contextmanager | |||
| @@ -24,6 +25,30 @@ class Generator(object): | |||
| self.site = site | |||
| self.__context__ = dict(site=site) | |||
| self.template = None | |||
| Plugin.load_all(site) | |||
| class PluginProxy(object): | |||
| """ | |||
| A proxy class to raise events in registered plugins | |||
| """ | |||
| def __init__(self, site): | |||
| super(PluginProxy, self).__init__() | |||
| self.site = site | |||
| def __getattr__(self, method_name): | |||
| if hasattr(Plugin, method_name): | |||
| def __call_plugins__(*args, **kwargs): | |||
| if self.site.plugins: | |||
| for plugin in self.site.plugins: | |||
| if hasattr(plugin, method_name): | |||
| function = getattr(plugin, method_name) | |||
| function(*args, **kwargs) | |||
| return __call_plugins__ | |||
| raise HydeException( | |||
| "Unknown plugin method [%s] called." % method_name) | |||
| self.events = PluginProxy(self.site) | |||
| @contextmanager | |||
| def context_for_resource(self, resource): | |||
| @@ -37,7 +62,7 @@ class Generator(object): | |||
| yield self.__context__ | |||
| self.__context__.update(resource=None) | |||
| def initialize_template_if_needed(self): | |||
| def load_template_if_needed(self): | |||
| """ | |||
| Loads and configures the template environement from the site | |||
| configuration if its not done already. | |||
| @@ -50,7 +75,16 @@ class Generator(object): | |||
| logger.info("Configuring the template environment") | |||
| self.template.configure(self.site.config) | |||
| def reload_if_needed(self): | |||
| self.events.template_loaded(self.template) | |||
| def initialize(self): | |||
| """ | |||
| Start Generation. Perform setup tasks and inform plugins. | |||
| """ | |||
| logger.info("Begin Generation") | |||
| self.events.begin_generation() | |||
| def load_site_if_needed(self): | |||
| """ | |||
| Checks if the site requries a reload and loads if | |||
| necessary. | |||
| @@ -60,25 +94,35 @@ class Generator(object): | |||
| logger.info("Reading site contents") | |||
| self.site.load() | |||
| def finalize(self): | |||
| """ | |||
| Generation complete. Inform plugins and cleanup. | |||
| """ | |||
| logger.info("Generation Complete") | |||
| self.events.generation_complete() | |||
| def generate_all(self): | |||
| """ | |||
| Generates the entire website | |||
| """ | |||
| logger.info("Reading site contents") | |||
| self.initialize_template_if_needed() | |||
| self.reload_if_needed() | |||
| self.load_template_if_needed() | |||
| self.initialize() | |||
| self.load_site_if_needed() | |||
| self.events.begin_site() | |||
| logger.info("Generating site to [%s]" % | |||
| self.site.config.deploy_root_path) | |||
| self.__generate_node__(self.site.content) | |||
| self.events.site_complete() | |||
| self.finalize() | |||
| def generate_node_at_path(self, node_path=None): | |||
| """ | |||
| Generates a single node. If node_path is non-existent or empty, | |||
| generates the entire site. | |||
| """ | |||
| self.initialize_template_if_needed() | |||
| self.reload_if_needed() | |||
| self.load_template_if_needed() | |||
| self.load_site_if_needed() | |||
| node = None | |||
| if node_path: | |||
| node = self.site.content.node_from_path(node_path) | |||
| @@ -89,12 +133,16 @@ class Generator(object): | |||
| Generates the given node. If node is invalid, empty or | |||
| non-existent, generates the entire website. | |||
| """ | |||
| self.initialize_template_if_needed() | |||
| self.reload_if_needed() | |||
| if not node: | |||
| return self.generate_all() | |||
| self.load_template_if_needed() | |||
| self.initialize() | |||
| self.load_site_if_needed() | |||
| try: | |||
| self.__generate_node__(node) | |||
| self.finalize() | |||
| except HydeException: | |||
| self.generate_all() | |||
| @@ -103,31 +151,39 @@ class Generator(object): | |||
| Generates a single resource. If resource_path is non-existent or empty, | |||
| generats the entire website. | |||
| """ | |||
| self.initialize_template_if_needed() | |||
| self.reload_if_needed() | |||
| self.load_template_if_needed() | |||
| self.load_site_if_needed() | |||
| resource = None | |||
| if resource_path: | |||
| resource = self.site.content.resource_from_path(resource_path) | |||
| return self.generate_resource(resource) | |||
| self.generate_resource(resource) | |||
| def generate_resource(self, resource=None): | |||
| """ | |||
| Generates the given resource. If resource is invalid, empty or | |||
| non-existent, generates the entire website. | |||
| """ | |||
| self.initialize_template_if_needed() | |||
| self.reload_if_needed() | |||
| if not resource: | |||
| return self.generate_all() | |||
| self.load_template_if_needed() | |||
| self.initialize() | |||
| self.load_site_if_needed() | |||
| try: | |||
| self.__generate_resource__(resource) | |||
| self.finalize() | |||
| except HydeException: | |||
| self.generate_all() | |||
| def __generate_node__(self, node): | |||
| logger.info("Generating [%s]", node) | |||
| for resource in node.walk_resources(): | |||
| self.__generate_resource__(resource) | |||
| for node in node.walk(): | |||
| self.events.begin_node(node) | |||
| for resource in node.resources: | |||
| self.__generate_resource__(resource) | |||
| self.events.node_complete(node) | |||
| def __generate_resource__(self, resource): | |||
| logger.info("Processing [%s]", resource) | |||
| @@ -137,10 +193,15 @@ class Generator(object): | |||
| self.site.content.source_folder)) | |||
| target.parent.make() | |||
| if resource.source_file.is_text: | |||
| text = resource.source_file.read_all() | |||
| text = self.events.begin_text_resource(resource, text) or text | |||
| logger.info("Rendering [%s]", resource) | |||
| text = self.template.render(resource.source_file.read_all(), | |||
| context) | |||
| text = self.template.render(text, context) | |||
| text = self.events.text_resource_complete( | |||
| resource, text) or text | |||
| target.write(text) | |||
| else: | |||
| logger.info("Copying binary file [%s]", resource) | |||
| self.events.begin_binary_resource(resource) | |||
| resource.source_file.copy_to(target) | |||
| self.events.binary_resource_complete(resource) | |||
| @@ -5,6 +5,11 @@ import sys | |||
| from hyde.exceptions import HydeException | |||
| import logging | |||
| from logging import NullHandler | |||
| logger = logging.getLogger('hyde.engine') | |||
| logger.addHandler(NullHandler()) | |||
| plugins = {} | |||
| templates = {} | |||
| @@ -17,6 +22,7 @@ def load_python_object(name): | |||
| if module_name == '': | |||
| (module_name, object_name) = (object_name, module_name) | |||
| try: | |||
| logger.info('Loading module [%s]' % module_name) | |||
| module = __import__(module_name) | |||
| except ImportError: | |||
| raise HydeException("The given module name [%s] is invalid." % | |||
| @@ -32,11 +38,13 @@ def load_python_object(name): | |||
| module_name) | |||
| try: | |||
| logger.info('Getting object [%s] from module [%s]' % | |||
| (object_name, module_name)) | |||
| return getattr(module, object_name) | |||
| except AttributeError: | |||
| raise HydeException("Cannot load the specified plugin [%s]. " | |||
| "The given module [%s] does not contain the " | |||
| "desired object [%s]. Please fix the" | |||
| "desired object [%s]. Please fix the " | |||
| "configuration or ensure that the module is " | |||
| "installed properly" % | |||
| (name, module_name, object_name)) | |||
| @@ -44,7 +44,8 @@ class Config(Expando): | |||
| media_root='media', | |||
| layout_root='layout', | |||
| media_url='/media', | |||
| site_url='/' | |||
| site_url='/', | |||
| plugins = [] | |||
| ) | |||
| conf = dict(**default_config) | |||
| if config_dict: | |||
| @@ -2,12 +2,14 @@ | |||
| """ | |||
| Contains definition for a plugin protocol and other utiltities. | |||
| """ | |||
| import abc | |||
| from hyde import loader | |||
| class Plugin(object): | |||
| """ | |||
| The plugin protocol | |||
| """ | |||
| __metaclass__ = abc.ABCMeta | |||
| def __init__(self, site): | |||
| super(Plugin, self).__init__() | |||
| @@ -19,60 +21,105 @@ class Plugin(object): | |||
| """ | |||
| pass | |||
| def prepare_site(self): | |||
| def begin_generation(self): | |||
| """ | |||
| Called when generation is about to take place. | |||
| """ | |||
| pass | |||
| def site_load_complete(self): | |||
| def begin_site(self): | |||
| """ | |||
| Called when the site is built complete. This implies that all the | |||
| Called when the site is loaded completely. This implies that all the | |||
| nodes and resources have been identified and are accessible in the | |||
| site variable. | |||
| """ | |||
| pass | |||
| def prepare_node(self, node): | |||
| def begin_node(self, node): | |||
| """ | |||
| Called when a node is about to be processed for generation. | |||
| This method is called only when the entire node is generated. | |||
| """ | |||
| pass | |||
| def prepare_resource(self, resource, text): | |||
| def begin_text_resource(self, resource, text): | |||
| """ | |||
| Called when a resource is about to be processed for generation. | |||
| The `text` parameter contains the, resource text at this point | |||
| Called when a text resource is about to be processed for generation. | |||
| The `text` parameter contains the resource text at this point | |||
| in its lifecycle. It is the text that has been loaded and any | |||
| plugins that are higher in the order may have tampered with it. | |||
| But the text has not been processed by the template yet. | |||
| But the text has not been processed by the template yet. Note that | |||
| the source file associated with the text resource may not be modifed | |||
| by any plugins. | |||
| If this function returns a value, it is used as the text for further | |||
| processing. | |||
| """ | |||
| return text | |||
| def process_resource(self, resource, text): | |||
| def begin_binary_resource(self, resource): | |||
| """ | |||
| Called when a binary resource is about to be processed for generation. | |||
| Plugins are free to modify the contents of the file. | |||
| """ | |||
| pass | |||
| def text_resource_complete(self, resource, text): | |||
| """ | |||
| Called when a resource has been processed by the template. | |||
| The `text` parameter contains the, resource text at this point | |||
| The `text` parameter contains the resource text at this point | |||
| in its lifecycle. It is the text that has been processed by the | |||
| template and any plugins that are higher in the order may have | |||
| tampered with it. | |||
| tampered with it. Note that the source file associated with the | |||
| text resource may not be modifed by any plugins. | |||
| If this function returns a value, it is used as the text for further | |||
| processing. | |||
| """ | |||
| return text | |||
| def binary_resource_complete(self, resource): | |||
| """ | |||
| Called when a binary resource has already been processed. | |||
| Plugins are free to modify the contents of the file. | |||
| """ | |||
| pass | |||
| def node_complete(self, node): | |||
| """ | |||
| Called when all the resources in the node have been processed. | |||
| This method is called only when the entire node is generated. | |||
| """ | |||
| pass | |||
| def site_complete(self): | |||
| """ | |||
| Called when the entire site has been processed. This method is called | |||
| only when the entire site is generated. | |||
| """ | |||
| pass | |||
| def site_complete(self): | |||
| """ | |||
| Called when the entire site has been processed. | |||
| Called when the generation process is complete. This method is called | |||
| only when the entire site is generated. | |||
| """ | |||
| pass | |||
| def generation_complete(self): | |||
| """ | |||
| Called when generation is completed. | |||
| """ | |||
| pass | |||
| @staticmethod | |||
| def load_all(site): | |||
| """ | |||
| Loads plugins based on the configuration. Assigns the plugins to | |||
| 'site.plugins' | |||
| """ | |||
| site.plugins = [loader.load_python_object(name)(site) | |||
| for name in site.config.plugins] | |||
| @@ -275,6 +275,7 @@ class Site(object): | |||
| self.sitepath = Folder(str(sitepath)) | |||
| self.config = config if config else Config(self.sitepath) | |||
| self.content = RootNode(self.config.content_root_path, self) | |||
| self.plugins = [] | |||
| def load(self): | |||
| """ | |||
| @@ -1,24 +1,24 @@ | |||
| <?xml version="1.0"?> | |||
| <!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> | |||
| <cross-domain-policy> | |||
| <!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html --> | |||
| <!-- Most restrictive policy: --> | |||
| <site-control permitted-cross-domain-policies="none"/> | |||
| <site-control permitted-cross-domain-policies="none"/> | |||
| <!-- Least restrictive policy: --> | |||
| <!-- | |||
| <site-control permitted-cross-domain-policies="all"/> | |||
| <allow-access-from domain="*" to-ports="*" secure="false"/> | |||
| <allow-http-request-headers-from domain="*" headers="*" secure="false"/> | |||
| <site-control permitted-cross-domain-policies="all"/> | |||
| <allow-access-from domain="*" to-ports="*" secure="false"/> | |||
| <allow-http-request-headers-from domain="*" headers="*" secure="false"/> | |||
| --> | |||
| <!-- | |||
| If you host a crossdomain.xml file with allow-access-from domain=“*” | |||
| and don’t understand all of the points described here, you probably | |||
| If you host a crossdomain.xml file with allow-access-from domain=“*” | |||
| and don’t understand all of the points described here, you probably | |||
| have a nasty security vulnerability. ~ simon willison | |||
| --> | |||
| @@ -109,6 +109,7 @@ HELPERS = File(JINJA2.child('helpers.html')) | |||
| INDEX = File(JINJA2.child('index.html')) | |||
| LAYOUT = File(JINJA2.child('layout.html')) | |||
| LOGO = File(TEMPLATE_ROOT.child('../../../resources/hyde-logo.png')) | |||
| XML = File(TEMPLATE_ROOT.child('../sites/test_jinja/content/crossdomain.xml')) | |||
| def test_ancestors(): | |||
| depth = 0 | |||
| @@ -156,6 +157,7 @@ def test_mimetype(): | |||
| def test_is_text(): | |||
| assert HELPERS.is_text | |||
| assert not LOGO.is_text | |||
| assert XML.is_text | |||
| def test_is_image(): | |||
| assert not HELPERS.is_image | |||
| @@ -62,7 +62,8 @@ class TestConfig(object): | |||
| assert getattr(c, name) == root | |||
| assert hasattr(c, path) | |||
| assert getattr(c, path) == TEST_SITE_ROOT.child_folder(root) | |||
| assert hasattr(c, 'plugins') | |||
| assert len(c.plugins) == 0 | |||
| assert c.deploy_root_path == TEST_SITE_ROOT.child_folder('deploy') | |||
| def test_conf1(self): | |||
| @@ -0,0 +1,246 @@ | |||
| # -*- coding: utf-8 -*- | |||
| """ | |||
| Use nose | |||
| `$ pip install nose` | |||
| `$ nosetests` | |||
| """ | |||
| from hyde.exceptions import HydeException | |||
| from hyde.fs import File, Folder | |||
| from hyde.generator import Generator | |||
| from hyde.plugin import Plugin | |||
| from hyde.site import Site | |||
| from mock import patch | |||
| from nose.tools import raises, nottest, with_setup | |||
| TEST_SITE = File(__file__).parent.child_folder('_test') | |||
| class PluginLoaderStub(Plugin): | |||
| pass | |||
| class TestPlugins(object): | |||
| @classmethod | |||
| def setup_class(cls): | |||
| TEST_SITE.make() | |||
| TEST_SITE.parent.child_folder('sites/test_jinja').copy_contents_to(TEST_SITE) | |||
| folders = [] | |||
| text_files = [] | |||
| binary_files = [] | |||
| with TEST_SITE.child_folder('content').walker as walker: | |||
| @walker.folder_visitor | |||
| def visit_folder(folder): | |||
| folders.append(folder.path) | |||
| @walker.file_visitor | |||
| def visit_file(afile): | |||
| if not afile.is_text: | |||
| binary_files.append(afile.path) | |||
| else: | |||
| text_files.append(afile.path) | |||
| cls.content_nodes = sorted(folders) | |||
| cls.content_text_resources = sorted(text_files) | |||
| cls.content_binary_resources = sorted(binary_files) | |||
| @classmethod | |||
| def teardown_class(cls): | |||
| TEST_SITE.delete() | |||
| def setUp(self): | |||
| self.site = Site(TEST_SITE) | |||
| self.site.config.plugins = ['hyde.tests.test_plugin.PluginLoaderStub'] | |||
| def test_can_load_plugin_modules(self): | |||
| assert not len(self.site.plugins) | |||
| Plugin.load_all(self.site) | |||
| assert len(self.site.plugins) == 1 | |||
| assert self.site.plugins[0].__class__.__name__ == 'PluginLoaderStub' | |||
| def test_generator_loads_plugins(self): | |||
| gen = Generator(self.site) | |||
| assert len(self.site.plugins) == 1 | |||
| def test_generator_template_registered_called(self): | |||
| with patch.object(PluginLoaderStub, 'template_loaded') as template_loaded_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert template_loaded_stub.call_count == 1 | |||
| def test_generator_template_begin_generation_called(self): | |||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert begin_generation_stub.call_count == 1 | |||
| def test_generator_template_begin_generation_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('about.html') | |||
| gen.generate_resource_at_path(path) | |||
| assert begin_generation_stub.call_count == 1 | |||
| def test_generator_template_begin_generation_called_for_single_node(self): | |||
| with patch.object(PluginLoaderStub, 'begin_generation') as begin_generation_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder | |||
| gen.generate_node_at_path(path) | |||
| assert begin_generation_stub.call_count == 1 | |||
| def test_generator_template_generation_complete_called(self): | |||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert generation_complete_stub.call_count == 1 | |||
| def test_generator_template_generation_complete_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('about.html') | |||
| gen.generate_resource_at_path(path) | |||
| assert generation_complete_stub.call_count == 1 | |||
| def test_generator_template_generation_complete_called_for_single_node(self): | |||
| with patch.object(PluginLoaderStub, 'generation_complete') as generation_complete_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder | |||
| gen.generate_node_at_path(path) | |||
| assert generation_complete_stub.call_count == 1 | |||
| def test_generator_template_begin_site_called(self): | |||
| with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert begin_site_stub.call_count == 1 | |||
| def test_generator_template_begin_site_not_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('about.html') | |||
| gen.generate_resource_at_path(path) | |||
| assert begin_site_stub.call_count == 0 | |||
| def test_generator_template_begin_site_not_called_for_single_node(self): | |||
| with patch.object(PluginLoaderStub, 'begin_site') as begin_site_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder | |||
| gen.generate_node_at_path(path) | |||
| assert begin_site_stub.call_count == 0 | |||
| def test_generator_template_site_complete_called(self): | |||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert site_complete_stub.call_count == 1 | |||
| def test_generator_template_site_complete_not_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('about.html') | |||
| gen.generate_resource_at_path(path) | |||
| assert site_complete_stub.call_count == 0 | |||
| def test_generator_template_site_complete_not_called_for_single_node(self): | |||
| with patch.object(PluginLoaderStub, 'site_complete') as site_complete_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder | |||
| gen.generate_node_at_path(path) | |||
| assert site_complete_stub.call_count == 0 | |||
| def test_generator_template_begin_node_called(self): | |||
| with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert begin_node_stub.call_count == len(self.content_nodes) | |||
| called_with_nodes = sorted([arg[0][0].path for arg in begin_node_stub.call_args_list]) | |||
| assert called_with_nodes == self.content_nodes | |||
| def test_generator_template_begin_node_not_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'begin_node') as begin_node_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||
| assert begin_node_stub.call_count == 0 | |||
| def test_generator_template_node_complete_called(self): | |||
| with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| assert node_complete_stub.call_count == len(self.content_nodes) | |||
| called_with_nodes = sorted([arg[0][0].path for arg in node_complete_stub.call_args_list]) | |||
| assert called_with_nodes == self.content_nodes | |||
| def test_generator_template_node_complete_not_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'node_complete') as node_complete_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_resource_at_path(self.site.content.source_folder.child('about.html')) | |||
| assert node_complete_stub.call_count == 0 | |||
| def test_generator_template_begin_text_resource_called(self): | |||
| with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||
| assert begin_text_resource_stub.call_count == len(self.content_text_resources) | |||
| assert called_with_resources == self.content_text_resources | |||
| def test_generator_template_begin_text_resource_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'begin_text_resource') as begin_text_resource_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('about.html') | |||
| gen.generate_resource_at_path(path) | |||
| called_with_resources = sorted([arg[0][0].path for arg in begin_text_resource_stub.call_args_list]) | |||
| assert begin_text_resource_stub.call_count == 1 | |||
| assert called_with_resources[0] == path | |||
| def test_generator_template_begin_binary_resource_called(self): | |||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||
| gen = Generator(self.site) | |||
| gen.generate_all() | |||
| called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||
| assert begin_binary_resource_stub.call_count == len(self.content_binary_resources) | |||
| assert called_with_resources == self.content_binary_resources | |||
| def test_generator_template_begin_binary_resource_called_for_single_resource(self): | |||
| with patch.object(PluginLoaderStub, 'begin_binary_resource') as begin_binary_resource_stub: | |||
| gen = Generator(self.site) | |||
| path = self.site.content.source_folder.child('favicon.ico') | |||
| gen.generate_resource_at_path(path) | |||
| called_with_resources = sorted([arg[0][0].path for arg in begin_binary_resource_stub.call_args_list]) | |||
| assert begin_binary_resource_stub.call_count == 1 | |||
| assert called_with_resources[0] == path | |||
| @@ -75,7 +75,8 @@ def test_walk_resources(): | |||
| "merry-christmas.html", | |||
| "crossdomain.xml", | |||
| "favicon.ico", | |||
| "robots.txt" | |||
| "robots.txt", | |||
| "site.css" | |||
| ] | |||
| pages.sort() | |||
| expected.sort() | |||