aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/README.rst92
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/base.py109
-rw-r--r--tests/data/translated_titles/conf.py11
-rw-r--r--tests/import_wordpress_and_build_workflow.py5
-rw-r--r--tests/test_command_import_wordpress.py90
-rw-r--r--tests/test_command_init.py12
-rw-r--r--tests/test_compile_markdown.py10
-rw-r--r--tests/test_integration.py221
-rw-r--r--tests/test_locale.py241
-rw-r--r--tests/test_plugin_importing.py8
-rw-r--r--tests/test_rss_feeds.py28
-rw-r--r--tests/test_rst_compiler.py (renamed from tests/test_rst_extensions.py)208
-rw-r--r--tests/test_scheduling.py126
-rw-r--r--tests/wordpress_export_example.xml59
15 files changed, 1087 insertions, 133 deletions
diff --git a/tests/README.rst b/tests/README.rst
new file mode 100644
index 0000000..2b3afb8
--- /dev/null
+++ b/tests/README.rst
@@ -0,0 +1,92 @@
+.. title: The Nikola Test Suite
+.. slug: tests
+.. date: 2012/03/30 23:00
+
+The Nikola Test Suite
+=====================
+
+Nikola, like many software projects, has a test suite. There are over 100
+tests.
+
+Tests (in alphabetical order)
+-----------------------------
+
+* ``test_command_import_wordpress`` tests the WordPress importer for
+ Nikola.
+* ``test_command_init`` checks whether new sites are created properly via the
+ ``init`` command.
+* ``test_compile_markdown`` exercises the Markdown compiler plugin of Nikola.
+* ``test_integration`` are used to validate that sites actually build.
+* ``test_locale`` tests the locale support of Nikola.
+* ``test_plugin_importing`` checks three basic plugins to know whether they
+ get imported properly.
+* ``test_rss_feeds`` asserts that RSS created by Nikola is sane.
+* ``test_rst_compiler`` exercises the reStructuredText compiler plugin of
+ Nikola.
+* ``test_scheduling`` performs tests on post scheduling rules.
+* ``test_utils`` test various Nikola utilities.
+
+Requirements to run the tests
+-----------------------------
+
+You need:
+
+* ``pip install -r requirements-tests.txt``
+* a few minutes’ time
+* appropriate locale settings
+
+How to set the locale for Nikola tests?
+---------------------------------------
+
+For testing nikola needs to specify two languages, each one with a supported locale. By default, the test suite uses ``en`` and ``es`` as languages, and their respective default locale for them.
+
+You can set the language - locale pairs by exporting two shell variables, like in::
+
+ export NIKOLA_LOCALE_DEFAULT=en,en_US.utf8
+ export NIKOLA_LOCALE_OTHER=es,es_ES.utf8
+
+In Windows that would be::
+
+ set NIKOLA_LOCALE_DEFAULT=en,English
+ set NIKOLA_LOCALE_OTHER=es,Spanish
+
+Replace the part before the comma with a Nikola translation selector (see ``nikola/conf.py.in`` for details), and the part after the comma with an *installed* glibc locale.
+
+To check if the desired locale is supported in your host you can, in a python console::
+
+ import locale
+ locale.setlocale(locale.LC_ALL, 'locale_name')
+ # by example, 'en_US.utf8' (posix) 'English' (windows)
+ # if it does not traceback, then python can use that locale
+
+Alternatively, if you have some disk space to spare, you can install
+the two default locales. Here is how to do that in Ubuntu::
+
+ sudo apt-get install language-pack-en language-pack-es
+
+
+How to execute the tests
+------------------------
+
+The command to execute tests is::
+
+ nosetests --with-coverage --cover-package=nikola --with-doctest --doctest-options=+NORMALIZE_WHITESPACE --logging-filter=-yapsy
+
+However, this command may change at any given moment. Check the
+``/.travis.yml`` file to get the current command.
+
+In Windows you want to drop the doctests parts, they fail over trivial differences in OS details.
+
+It is also recommended to run ``nikola help`` to see if Nikola actually
+works.
+
+If you are committing code, make sure to run ``flake8 --ignore=E501 .`` to see if you comply with the PEP 8 style guide and do not have basic code mistakes (we ignore the 79-characters-per-line rule).
+
+In windows ignore the two flake8 diagnostics about messages_sl_si.py , they are artifacts of (symlinks + git + windows).
+
+
+Travis CI
+---------
+
+We also run our tests on `Travis CI <https://travis-ci.org/>`_.
+You can check the `current build status <https://travis-ci.org/getnikola/nikola>`_ there.
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/base.py b/tests/base.py
index 92576c7..00f7486 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -6,12 +6,21 @@
""" Base class for Nikola test cases """
-__all__ = ["BaseTestCase"]
-
+__all__ = ["BaseTestCase", "cd", "LocaleSupportInTesting"]
+from contextlib import contextmanager
+import locale
+import os
import sys
import unittest
+import logbook
+
+# Make logbook shutup
+import nikola.utils
+
+nikola.utils.LOGGER.handlers.append(logbook.TestHandler())
+
if sys.version_info < (2, 7):
@@ -56,3 +65,99 @@ if sys.version_info < (2, 7):
else:
BaseTestCase = unittest.TestCase
+
+
+@contextmanager
+def cd(path):
+ old_dir = os.getcwd()
+ os.chdir(path)
+ yield
+ os.chdir(old_dir)
+
+
+class LocaleSupportInTesting(object):
+ """
+ Nikola needs two pairs of valid (language, locale_n) to test multilingual sites.
+
+ As languages of interest and installed OS support varies from host to host
+ we allow to specify two such pairs.
+
+ A valid pair complies
+ 'languaje' one of the names of nikola translations ('en', 'es', ...)
+ 'locale_n' is a string that python accepts to set a locale, like in
+ import locale
+ locale.setlocale(locale.LC_ALL, str(locale_n))
+
+ You specify the custom pairs to use with two environment variables
+ NIKOLA_LOCALE_DEFAULT (lang and locale to use as nikola's DEFAULT_LANG)
+ NIKOLA_LOCALE_OTHER
+
+ The value of the pair is lang (as in keys of Nikola's TRANSLATIONS), followed
+ by coma, followed by the locale.
+ """
+
+ @classmethod
+ def initialize(cls):
+ """Determines and diagnoses the two (lang, locale) pairs to use in testing
+
+ While it only needs to run once at the beginning of the testing session,
+ calling multiple times is fine.
+ """
+ if hasattr(cls, 'langlocales'):
+ return
+ defaults = {
+ 'linux': {
+ # non-windows defaults, must be two locales suported by .travis.yml
+ 'default': ("en", str("en_US.utf8")),
+ 'other': ("es", str("es_ES.utf8")),
+ },
+ 'windows': {
+ # windows defaults
+ 'default': ("en", str("English")),
+ 'other': ("es", str("Spanish")),
+ },
+ }
+ os_id = 'windows' if sys.platform == 'win32' else 'linux'
+ langlocales = {}
+ for suffix in ['other', 'default']:
+ try:
+ envar = 'NIKOLA_LOCALE_' + suffix.upper()
+ s = os.environ[envar]
+ parts = s.split(',')
+ lang = parts[0].strip()
+ try:
+ locale_n = str(parts[1].strip())
+ locale.setlocale(locale.LC_ALL, locale_n)
+ except Exception:
+ msg = ("Environment variable {0} fails to specify a valid <lang>,<locale>." +
+ "Check your syntax, check that python supports that locale in your host.")
+ nikola.utils.LOGGER.error(msg.format(envar))
+ sys.exit(1)
+ except KeyError:
+ lang, locale_n = defaults[os_id][suffix]
+ langlocales[suffix] = (lang, locale_n)
+ if (langlocales['default'][0] == langlocales['other'][0] or
+ langlocales['default'][1] == langlocales['other'][1]): # NOQA
+ # the mix of defaults and enviro is not good
+ msg = ('Locales for testing should differ in lang and locale, else ' +
+ 'the test would we weak. Check your environment settings for ' +
+ 'NIKOLA_LOCALE_DEFAULT and NIKOLA_LOCALE_OTHER')
+ nikola.utils.LOGGER.error(msg)
+ setattr(cls, 'langlocales', langlocales)
+
+ @classmethod
+ def initialize_locales_for_testing(cls, variant):
+ """initializes nikola.utils.LocaleBorg"""
+ if not hasattr(cls, 'langlocales'):
+ cls.initialize()
+ default_lang = cls.langlocales['default'][0]
+ locales = {}
+ locales[default_lang] = cls.langlocales['default'][1]
+ if variant == 'unilingual':
+ pass
+ elif variant == 'bilingual':
+ locales[cls.langlocales['other'][0]] = cls.langlocales['other'][1]
+ else:
+ raise ValueError('Unknown locale variant')
+ nikola.utils.LocaleBorg.reset()
+ nikola.utils.LocaleBorg.initialize(locales, default_lang)
diff --git a/tests/data/translated_titles/conf.py b/tests/data/translated_titles/conf.py
index 69c7bc7..b445ba9 100644
--- a/tests/data/translated_titles/conf.py
+++ b/tests/data/translated_titles/conf.py
@@ -1,4 +1,3 @@
-
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
@@ -72,7 +71,7 @@ SIDEBAR_LINKS = {
# The wildcard is used to generate a list of reSt source files
# (whatever/thing.txt).
# That fragment must have an associated metadata file (whatever/thing.meta),
-# and opcionally translated files (example for spanish, with code "es"):
+# and optionally translated files (example for spanish, with code "es"):
# whatever/thing.txt.es and whatever/thing.meta.es
#
# From those files, a set of HTML fragment files will be generated:
@@ -106,7 +105,7 @@ post_pages = (
# 'rest' is reStructuredText
# 'markdown' is MarkDown
# 'html' assumes the file is html and just copies it
-post_compilers = {
+COMPILERS = {
"rest": ('.txt', '.rst'),
"markdown": ('.md', '.mdown', '.markdown'),
"textile": ('.textile',),
@@ -340,10 +339,14 @@ CONTENT_FOOTER = CONTENT_FOOTER.format(email=BLOG_EMAIL,
# external resources.
# USE_CDN = False
-# Google analytics or whatever else you use. Added to the bottom of <body>
+# Google analytics script or whatever else you use. Added to the bottom of <body>
# in the default template (base.tmpl).
# ANALYTICS = ""
+# HTML snippet that will be added at the bottom of body of <body>
+# in the default template (base.tmpl).
+# SOCIAL_BUTTONS_CODE = ""
+
# The possibility to extract metadata from the filename by using a
# regular expression.
# To make it work you need to name parts of your regular expression.
diff --git a/tests/import_wordpress_and_build_workflow.py b/tests/import_wordpress_and_build_workflow.py
index 90cb6a8..bc04a1f 100644
--- a/tests/import_wordpress_and_build_workflow.py
+++ b/tests/import_wordpress_and_build_workflow.py
@@ -26,12 +26,13 @@ def main(import_directory=None):
test_directory = os.path.dirname(__file__)
package_directory = os.path.abspath(os.path.join(test_directory, '..'))
- os.system('echo "y" | pip uninstall Nikola')
+ os.system('pip uninstall -y Nikola')
os.system('pip install %s' % package_directory)
os.system('nikola')
import_file = os.path.join(test_directory, 'wordpress_export_example.xml')
os.system(
- 'nikola import_wordpress -f %s -o %s' % (import_file, import_directory))
+ 'nikola import_wordpress -o {folder} {file}'.format(file=import_file,
+ folder=import_directory))
assert os.path.exists(
import_directory), "The directory %s should be existing."
diff --git a/tests/test_command_import_wordpress.py b/tests/test_command_import_wordpress.py
index 3be2ad9..f215705 100644
--- a/tests/test_command_import_wordpress.py
+++ b/tests/test_command_import_wordpress.py
@@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
+from __future__ import unicode_literals, absolute_import
-from context import nikola
+from .context import nikola
import os
import unittest
import mock
+import nikola.plugins.command.import_wordpress
+from .base import BaseTestCase
-class BasicCommandImportWordpress(unittest.TestCase):
+
+class BasicCommandImportWordpress(BaseTestCase):
def setUp(self):
- self.import_command = nikola.plugins.command_import_wordpress.CommandImportWordpress()
+ self.module = nikola.plugins.command.import_wordpress
+ self.import_command = self.module.CommandImportWordpress()
self.import_filename = os.path.abspath(os.path.join(
os.path.dirname(__file__), 'wordpress_export_example.xml'))
@@ -51,11 +55,11 @@ class CommandImportWordpressRunTest(BasicCommandImportWordpress):
site_generation_patch = mock.patch('os.system', self.site_generation)
data_import_patch = mock.patch(
- 'nikola.plugins.command_import_wordpress.CommandImportWordpress.import_posts', self.data_import)
+ 'nikola.plugins.command.import_wordpress.CommandImportWordpress.import_posts', self.data_import)
write_urlmap_patch = mock.patch(
- 'nikola.plugins.command_import_wordpress.CommandImportWordpress.write_urlmap_csv', self.write_urlmap)
+ 'nikola.plugins.command.import_wordpress.CommandImportWordpress.write_urlmap_csv', self.write_urlmap)
write_configuration_patch = mock.patch(
- 'nikola.plugins.command_import_wordpress.CommandImportWordpress.write_configuration', self.write_configuration)
+ 'nikola.plugins.command.import_wordpress.CommandImportWordpress.write_configuration', self.write_configuration)
self.patches = [site_generation_patch, data_import_patch,
write_urlmap_patch, write_configuration_patch]
@@ -117,7 +121,7 @@ class CommandImportWordpressTest(BasicCommandImportWordpress):
self.import_filename)
context = self.import_command.populate_context(channel)
- for required_key in ('POST_PAGES', 'POST_COMPILERS'):
+ for required_key in ('POSTS', 'PAGES', 'COMPILERS'):
self.assertTrue(required_key in context)
self.assertEqual('de', context['DEFAULT_LANG'])
@@ -133,33 +137,37 @@ class CommandImportWordpressTest(BasicCommandImportWordpress):
self.import_filename)
self.import_command.context = self.import_command.populate_context(
channel)
- self.import_command.url_map = {} # For testing we use an empty one.
self.import_command.output_folder = 'new_site'
self.import_command.squash_newlines = True
self.import_command.no_downloads = False
+ # Ensuring clean results
+ self.import_command.url_map = {}
+ self.module.links = {}
+
write_metadata = mock.MagicMock()
write_content = mock.MagicMock()
download_mock = mock.MagicMock()
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_content', write_content):
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.write_metadata', write_metadata):
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.download_url_content_to_file', download_mock):
- with mock.patch('nikola.plugins.command_import_wordpress.os.makedirs'):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.write_content', write_content):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.write_metadata', write_metadata):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.download_url_content_to_file', download_mock):
+ with mock.patch('nikola.plugins.command.import_wordpress.os.makedirs'):
self.import_command.import_posts(channel)
self.assertTrue(download_mock.called)
+ qpath = 'new_site/files/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png'
download_mock.assert_any_call(
'http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png',
- 'new_site/files/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png')
+ qpath.replace('/', os.sep))
self.assertTrue(write_metadata.called)
write_metadata.assert_any_call(
- 'new_site/stories/kontakt.meta', 'Kontakt',
+ 'new_site/stories/kontakt.meta'.replace('/', os.sep), 'Kontakt',
'kontakt', '2009-07-16 20:20:32', None, [])
self.assertTrue(write_content.called)
- write_content.assert_any_call('new_site/posts/200704hoert.wp',
+ write_content.assert_any_call('new_site/posts/200704hoert.wp'.replace('/', os.sep),
"""An image.
<img class="size-full wp-image-16" title="caption test" src="http://some.blog/wp-content/uploads/2009/07/caption_test.jpg" alt="caption test" width="739" height="517" />
@@ -179,11 +187,11 @@ The end.
""")
write_content.assert_any_call(
- 'new_site/posts/200807arzt-und-pfusch-s-i-c-k.wp',
+ 'new_site/posts/200807arzt-und-pfusch-s-i-c-k.wp'.replace('/', os.sep),
'''<img class="size-full wp-image-10 alignright" title="Arzt+Pfusch - S.I.C.K." src="http://some.blog/wp-content/uploads/2008/07/arzt_und_pfusch-sick-cover.png" alt="Arzt+Pfusch - S.I.C.K." width="210" height="209" />Arzt+Pfusch - S.I.C.K.Gerade bin ich \xfcber das Album <em>S.I.C.K</em> von <a title="Arzt+Pfusch" href="http://www.arztpfusch.com/" target="_blank">Arzt+Pfusch</a> gestolpert, welches Arzt+Pfusch zum Download f\xfcr lau anbieten. Das Album steht unter einer Creative Commons <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/">BY-NC-ND</a>-Lizenz.
Die Ladung <em>noisebmstupidevildustrial</em> gibts als MP3s mit <a href="http://www.archive.org/download/dmp005/dmp005_64kb_mp3.zip">64kbps</a> und <a href="http://www.archive.org/download/dmp005/dmp005_vbr_mp3.zip">VBR</a>, als Ogg Vorbis und als FLAC (letztere <a href="http://www.archive.org/details/dmp005">hier</a>). <a href="http://www.archive.org/download/dmp005/dmp005-artwork.zip">Artwork</a> und <a href="http://www.archive.org/download/dmp005/dmp005-lyrics.txt">Lyrics</a> gibts nochmal einzeln zum Download.''')
write_content.assert_any_call(
- 'new_site/stories/kontakt.wp', """<h1>Datenschutz</h1>
+ 'new_site/stories/kontakt.wp'.replace('/', os.sep), """<h1>Datenschutz</h1>
Ich erhebe und speichere automatisch in meine Server Log Files Informationen, die dein Browser an mich \xfcbermittelt. Dies sind:
<ul>
@@ -209,15 +217,36 @@ Diese Daten sind f\xfcr mich nicht bestimmten Personen zuordenbar. Eine Zusammen
self.import_command.url_map['http://some.blog/kontakt/'],
'http://some.blog/stories/kontakt.html')
+ image_thumbnails = [
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-64x64.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-300x175.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-36x36.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-24x24.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-96x96.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-96x96.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-48x48.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-96x96.png',
+ 'http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-150x150.png'
+ ]
+
+ for link in image_thumbnails:
+ self.assertTrue(
+ link in self.module.links,
+ 'No link to "{0}" found in {map}.'.format(
+ link,
+ map=self.module.links
+ )
+ )
+
def test_transforming_content(self):
"""Applying markup conversions to content."""
transform_sourcecode = mock.MagicMock()
transform_caption = mock.MagicMock()
transform_newlines = mock.MagicMock()
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.transform_sourcecode', transform_sourcecode):
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.transform_caption', transform_caption):
- with mock.patch('nikola.plugins.command_import_wordpress.CommandImportWordpress.transform_multiple_newlines', transform_newlines):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.transform_sourcecode', transform_sourcecode):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.transform_caption', transform_caption):
+ with mock.patch('nikola.plugins.command.import_wordpress.CommandImportWordpress.transform_multiple_newlines', transform_newlines):
self.import_command.transform_content("random content")
self.assertTrue(transform_sourcecode.called)
@@ -327,7 +356,7 @@ newlines.
config_path_with_timestamp = self.import_command.get_configuration_output_path(
)
self.assertNotEqual(default_config_path, config_path_with_timestamp)
- self.assertTrue('wordpress_import' in config_path_with_timestamp)
+ self.assertTrue(self.import_command.name in config_path_with_timestamp)
def test_write_content_does_not_detroy_text(self):
content = b"""<h1>Installation</h1>
@@ -338,7 +367,7 @@ There are many plugins.
<h2>Violations</h2>
You can use the <a title="Jenkins Plugin: Violations" href="https://wiki.jenkins-ci.org/display/JENKINS/Violations">Violations</a> plugin."""
open_mock = mock.mock_open()
- with mock.patch('nikola.plugins.command_import_wordpress.open', open_mock, create=True):
+ with mock.patch('nikola.plugins.basic_import.open', open_mock, create=True):
self.import_command.write_content('some_file', content)
open_mock.assert_called_once_with('some_file', 'wb+')
@@ -346,5 +375,20 @@ You can use the <a title="Jenkins Plugin: Violations" href="https://wiki.jenkins
call_context.write.assert_called_once_with(
content.join([b'<html><body>', b'</body></html>']))
+ def test_configure_redirections(self):
+ """
+ Testing the configuration of the redirections.
+
+ We need to make sure that we have valid sources and target links.
+ """
+ url_map = {
+ '/somewhere/else': 'http://foo.bar/posts/somewhereelse.html'
+ }
+
+ redirections = self.import_command.configure_redirections(url_map)
+
+ self.assertEqual(1, len(redirections))
+ self.assertTrue(('somewhere/else/index.html', '/posts/somewhereelse.html') in redirections)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_command_init.py b/tests/test_command_init.py
index 1904fa1..a9ec208 100644
--- a/tests/test_command_init.py
+++ b/tests/test_command_init.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
+from __future__ import unicode_literals, absolute_import
-from context import nikola
+from .context import nikola
import unittest
import mock
@@ -12,18 +12,18 @@ class CommandInitCallTest(unittest.TestCase):
self.create_configuration = mock.MagicMock()
self.create_empty_site = mock.MagicMock()
copy_sample_site_patch = mock.patch(
- 'nikola.plugins.command_init.CommandInit.copy_sample_site', self.copy_sample_site)
+ 'nikola.plugins.command.init.CommandInit.copy_sample_site', self.copy_sample_site)
create_configuration_patch = mock.patch(
- 'nikola.plugins.command_init.CommandInit.create_configuration', self.create_configuration)
+ 'nikola.plugins.command.init.CommandInit.create_configuration', self.create_configuration)
create_empty_site_patch = mock.patch(
- 'nikola.plugins.command_init.CommandInit.create_empty_site', self.create_empty_site)
+ 'nikola.plugins.command.init.CommandInit.create_empty_site', self.create_empty_site)
self.patches = [copy_sample_site_patch,
create_configuration_patch, create_empty_site_patch]
for patch in self.patches:
patch.start()
- self.init_commad = nikola.plugins.command_init.CommandInit()
+ self.init_commad = nikola.plugins.command.init.CommandInit()
def tearDown(self):
for patch in self.patches:
diff --git a/tests/test_compile_markdown.py b/tests/test_compile_markdown.py
index a1f8591..a8252d8 100644
--- a/tests/test_compile_markdown.py
+++ b/tests/test_compile_markdown.py
@@ -6,7 +6,14 @@ import tempfile
import unittest
from os import path
-from nikola.plugins.compile_markdown import CompileMarkdown
+from nikola.plugins.compile.markdown import CompileMarkdown
+
+
+class FakeSite(object):
+ config = {
+ "MARKDOWN_EXTENSIONS": ['fenced_code', 'codehilite'],
+ "LOGGING_HANDLERS": {'stderr': {'loglevel': 'WARNING', 'bubble': True}}
+ }
class CompileMarkdownTests(unittest.TestCase):
@@ -16,6 +23,7 @@ class CompileMarkdownTests(unittest.TestCase):
self.output_path = path.join(self.tmp_dir, 'output.html')
self.compiler = CompileMarkdown()
+ self.compiler.set_site(FakeSite())
def compile(self, input_string):
with codecs.open(self.input_path, "w+", "utf8") as input_file:
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 802dcc7..fdb2494 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -1,44 +1,40 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals, print_function
+from __future__ import unicode_literals, print_function, absolute_import
import codecs
-from contextlib import contextmanager
import locale
import os
import shutil
-import subprocess # NOQA
+import subprocess
+import sys
import tempfile
import unittest
import lxml.html
from nose.plugins.skip import SkipTest
-from context import nikola
from nikola import main
+import nikola
+import nikola.plugins.command
+import nikola.plugins.command.init
+from .base import BaseTestCase, cd
-@contextmanager
-def cd(path):
- old_dir = os.getcwd()
- os.chdir(path)
- yield
- os.chdir(old_dir)
-
-class EmptyBuildTest(unittest.TestCase):
+class EmptyBuildTest(BaseTestCase):
"""Basic integration testcase."""
dataname = None
@classmethod
- def setUpClass(self):
+ def setUpClass(cls):
"""Setup a demo site."""
- self.tmpdir = tempfile.mkdtemp()
- self.target_dir = os.path.join(self.tmpdir, "target")
- self.init_command = nikola.plugins.command_init.CommandInit()
- self.fill_site()
- self.patch_site()
- self.build()
+ cls.tmpdir = tempfile.mkdtemp()
+ cls.target_dir = os.path.join(cls.tmpdir, "target")
+ cls.init_command = nikola.plugins.command.init.CommandInit()
+ cls.fill_site()
+ cls.patch_site()
+ cls.build()
@classmethod
def fill_site(self):
@@ -69,7 +65,13 @@ class EmptyBuildTest(unittest.TestCase):
@classmethod
def tearDownClass(self):
"""Remove the demo site."""
- shutil.rmtree(self.tmpdir)
+ # ignore_errors=True for windows by issue #782
+ shutil.rmtree(self.tmpdir, ignore_errors=(sys.platform == 'win32'))
+ # Fixes Issue #438
+ try:
+ del sys.modules['conf']
+ except KeyError:
+ pass
def test_build(self):
"""Ensure the build did something."""
@@ -91,9 +93,82 @@ class DemoBuildTest(EmptyBuildTest):
outf.write(
".. title: foobar\n"
".. slug: foobar\n"
- ".. date: 2013/03/06 19:08:15\n"
+ ".. date: 2013-03-06 19:08:15\n"
+ )
+
+ def test_index_in_sitemap(self):
+ sitemap_path = os.path.join(self.target_dir, "output", "sitemap.xml")
+ sitemap_data = codecs.open(sitemap_path, "r", "utf8").read()
+ self.assertTrue('<loc>http://getnikola.com/index.html</loc>' in sitemap_data)
+
+ def test_avoid_double_slash_in_rss(self):
+ rss_path = os.path.join(self.target_dir, "output", "rss.xml")
+ rss_data = codecs.open(rss_path, "r", "utf8").read()
+ self.assertFalse('http://getnikola.com//' in rss_data)
+
+
+class RepeatedPostsSetting(DemoBuildTest):
+ """Duplicate POSTS, should not read each post twice, which causes conflicts."""
+ @classmethod
+ def patch_site(self):
+ """Set the SITE_URL to have a path"""
+ conf_path = os.path.join(self.target_dir, "conf.py")
+ with codecs.open(conf_path, "ab", "utf8") as outf:
+ outf.write('\nPOSTS = (("posts/*.txt", "posts", "post.tmpl"),("posts/*.txt", "posts", "post.tmpl"))\n')
+
+
+class FuturePostTest(EmptyBuildTest):
+ """Test a site with future posts."""
+
+ @classmethod
+ def fill_site(self):
+ import datetime
+ from nikola.utils import current_time
+ self.init_command.copy_sample_site(self.target_dir)
+ self.init_command.create_configuration(self.target_dir)
+
+ # Change COMMENT_SYSTEM_ID to not wait for 5 seconds
+ with codecs.open(os.path.join(self.target_dir, 'conf.py'), "ab+", "utf8") as outf:
+ outf.write('\nCOMMENT_SYSTEM_ID = "nikolatest"\n')
+
+ with codecs.open(os.path.join(self.target_dir, 'posts', 'empty1.txt'), "wb+", "utf8") as outf:
+ outf.write(
+ ".. title: foo\n"
+ ".. slug: foo\n"
+ ".. date: %s\n" % (current_time() + datetime.timedelta(-1)).strftime('%Y-%m-%d %H:%M:%S')
)
+ with codecs.open(os.path.join(self.target_dir, 'posts', 'empty2.txt'), "wb+", "utf8") as outf:
+ outf.write(
+ ".. title: bar\n"
+ ".. slug: bar\n"
+ ".. date: %s\n" % (current_time() + datetime.timedelta(1)).strftime('%Y-%m-%d %H:%M:%S')
+ )
+
+ def test_future_post(self):
+ """ Ensure that the future post is not present in the index and sitemap."""
+ index_path = os.path.join(self.target_dir, "output", "index.html")
+ sitemap_path = os.path.join(self.target_dir, "output", "sitemap.xml")
+ foo_path = os.path.join(self.target_dir, "output", "posts", "foo.html")
+ bar_path = os.path.join(self.target_dir, "output", "posts", "bar.html")
+ self.assertTrue(os.path.isfile(index_path))
+ self.assertTrue(os.path.isfile(foo_path))
+ self.assertTrue(os.path.isfile(bar_path))
+ index_data = codecs.open(index_path, "r", "utf8").read()
+ sitemap_data = codecs.open(sitemap_path, "r", "utf8").read()
+ self.assertTrue('foo.html' in index_data)
+ self.assertFalse('bar.html' in index_data)
+ self.assertTrue('foo.html' in sitemap_data)
+ self.assertFalse('bar.html' in sitemap_data)
+
+ # Run deploy command to see if future post is deleted
+ with cd(self.target_dir):
+ main.main(["deploy"])
+
+ self.assertTrue(os.path.isfile(index_path))
+ self.assertTrue(os.path.isfile(foo_path))
+ self.assertFalse(os.path.isfile(bar_path))
+
class TranslatedBuildTest(EmptyBuildTest):
"""Test a site with translated content."""
@@ -132,8 +207,8 @@ class RelativeLinkTest(DemoBuildTest):
conf_path = os.path.join(self.target_dir, "conf.py")
with codecs.open(conf_path, "rb", "utf-8") as inf:
data = inf.read()
- data = data.replace('SITE_URL = "http://nikola.ralsina.com.ar"',
- 'SITE_URL = "http://nikola.ralsina.com.ar/foo/bar/"')
+ data = data.replace('SITE_URL = "http://getnikola.com/"',
+ 'SITE_URL = "http://getnikola.com/foo/bar/"')
with codecs.open(conf_path, "wb+", "utf8") as outf:
outf.write(data)
@@ -151,36 +226,51 @@ class RelativeLinkTest(DemoBuildTest):
# But I also need to be sure it is there!
self.assertTrue(flag)
+ def test_index_in_sitemap(self):
+ """Test that the correct path is in sitemap, and not the wrong one."""
+ sitemap_path = os.path.join(self.target_dir, "output", "sitemap.xml")
+ sitemap_data = codecs.open(sitemap_path, "r", "utf8").read()
+ self.assertFalse('<loc>http://getnikola.com/</loc>' in sitemap_data)
+ self.assertTrue('<loc>http://getnikola.com/foo/bar/index.html</loc>' in sitemap_data)
+
-#class TestCheck(DemoBuildTest):
- #"""The demo build should pass 'nikola check'"""
+if sys.version_info[0] == 2 and sys.version_info[1] < 7:
+ check_output = subprocess.check_call
+else:
+ check_output = subprocess.check_output
- #def test_check_links(self):
- #with cd(self.target_dir):
- #r = subprocess.call("nikola check -l", shell=True)
- #self.assertEqual(r, 0)
- #def test_check_files(self):
- #with cd(self.target_dir):
- #r = subprocess.call("nikola check -f", shell=True)
- #self.assertEqual(r, 0)
+class TestCheck(DemoBuildTest):
+ """The demo build should pass 'nikola check'"""
+ def test_check_links(self):
+ with cd(self.target_dir):
+ check_output("nikola check -l", shell=True, stderr=subprocess.STDOUT)
-#class TestCheckFailure(DemoBuildTest):
- #"""The demo build should pass 'nikola check'"""
+ def test_check_files(self):
+ with cd(self.target_dir):
+ check_output("nikola check -f", shell=True, stderr=subprocess.STDOUT)
- #def test_check_links_fail(self):
- #with cd(self.target_dir):
- #os.unlink(os.path.join("output", "archive.html"))
- #rv = subprocess.call("nikola check -l", shell=True)
- #self.assertEqual(rv, 1)
- #def test_check_files_fail(self):
- #with cd(self.target_dir):
- #with codecs.open(os.path.join("output", "foobar"), "wb+", "utf8") as outf:
- #outf.write("foo")
- #rv = subprocess.call("nikola check -f", shell=True)
- #self.assertEqual(rv, 1)
+class TestCheckFailure(DemoBuildTest):
+ """The demo build should pass 'nikola check'"""
+
+ def test_check_links_fail(self):
+ with cd(self.target_dir):
+ os.unlink(os.path.join("output", "archive.html"))
+ self.assertRaises(
+ subprocess.CalledProcessError,
+ check_output, "nikola check -f", shell=True
+ )
+
+ def test_check_files_fail(self):
+ with cd(self.target_dir):
+ with codecs.open(os.path.join("output", "foobar"), "wb+", "utf8") as outf:
+ outf.write("foo")
+ self.assertRaises(
+ subprocess.CalledProcessError,
+ check_output, "nikola check -f", shell=True
+ )
class RelativeLinkTest2(DemoBuildTest):
@@ -192,8 +282,10 @@ class RelativeLinkTest2(DemoBuildTest):
conf_path = os.path.join(self.target_dir, "conf.py")
with codecs.open(conf_path, "rb", "utf-8") as inf:
data = inf.read()
- data = data.replace('("stories/*.txt", "stories", "story.tmpl", False),',
- '("stories/*.txt", "", "story.tmpl", False),')
+ data = data.replace('("stories/*.txt", "stories", "story.tmpl"),',
+ '("stories/*.txt", "", "story.tmpl"),')
+ data = data.replace('("stories/*.rst", "stories", "story.tmpl"),',
+ '("stories/*.rst", "", "story.tmpl"),')
data = data.replace('# INDEX_PATH = ""',
'INDEX_PATH = "blog"')
with codecs.open(conf_path, "wb+", "utf8") as outf:
@@ -202,8 +294,6 @@ class RelativeLinkTest2(DemoBuildTest):
def test_relative_links(self):
"""Check that the links in a story are correct"""
- conf_path = os.path.join(self.target_dir, "conf.py")
- data = open(conf_path).read()
test_path = os.path.join(self.target_dir, "output", "about-nikola.html")
flag = False
with open(test_path, "rb") as inf:
@@ -215,3 +305,34 @@ class RelativeLinkTest2(DemoBuildTest):
flag = True
# But I also need to be sure it is there!
self.assertTrue(flag)
+
+ def test_index_in_sitemap(self):
+ """Test that the correct path is in sitemap, and not the wrong one."""
+ sitemap_path = os.path.join(self.target_dir, "output", "sitemap.xml")
+ sitemap_data = codecs.open(sitemap_path, "r", "utf8").read()
+ self.assertFalse('<loc>http://getnikola.com/</loc>' in sitemap_data)
+ self.assertTrue('<loc>http://getnikola.com/blog/index.html</loc>' in sitemap_data)
+
+
+class MonthlyArchiveTest(DemoBuildTest):
+ """Check that the monthly archives build and are correct."""
+
+ @classmethod
+ def patch_site(self):
+ """Set the SITE_URL to have a path"""
+ conf_path = os.path.join(self.target_dir, "conf.py")
+ with codecs.open(conf_path, "rb", "utf-8") as inf:
+ data = inf.read()
+ data = data.replace('# CREATE_MONTHLY_ARCHIVE = False',
+ 'CREATE_MONTHLY_ARCHIVE = True')
+ with codecs.open(conf_path, "wb+", "utf8") as outf:
+ outf.write(data)
+ outf.flush()
+
+ def test_monthly_archive(self):
+ """See that it builds"""
+ self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'target', 'output', '2012', '03', 'index.html')))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_locale.py b/tests/test_locale.py
new file mode 100644
index 0000000..93df2ae
--- /dev/null
+++ b/tests/test_locale.py
@@ -0,0 +1,241 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+import sys
+
+# needed if @unittest.expectedFailure is used
+try:
+ import unittest2 as unittest
+except:
+ import unittest
+
+import nikola.nikola
+import nikola.utils
+from .base import LocaleSupportInTesting
+
+LocaleSupportInTesting.initialize_locales_for_testing('bilingual')
+lang_11, loc_11 = LocaleSupportInTesting.langlocales['default']
+lang_22, loc_22 = LocaleSupportInTesting.langlocales['other']
+
+
+# these are candidates to hardcoded locales, using str() for py2x setlocale
+loc_C = str('C')
+loc_Cutf8 = str('C.utf8')
+
+if sys.platform != 'win32':
+ nikola.nikola.workaround_empty_LC_ALL_posix()
+
+
+class TestHarcodedFallbacks(unittest.TestCase):
+ def test_hardcoded_fallbacks_work(self):
+ # keep in sync with nikola.valid_locale_fallback
+ if sys.platform == 'win32':
+ self.assertTrue(nikola.nikola.is_valid_locale(str('English')))
+ self.assertTrue(nikola.nikola.is_valid_locale(str('C')))
+ else:
+ # the 1st is desired in Travis, not a problem if fails in user host
+ self.assertTrue(nikola.nikola.is_valid_locale(str('en_US.utf8')))
+ # this is supposed to be always valid, and we need an universal
+ # fallback. Failure is not a problem in user host if he / she
+ # sets a valid (in his host) locale_fallback.
+ self.assertTrue(nikola.nikola.is_valid_locale(str('C')))
+
+
+class TestConfigLocale(unittest.TestCase):
+
+ def test_implicit_fallback(self):
+ locale_fallback = None
+ sanitized_fallback = nikola.nikola.valid_locale_fallback(
+ desired_locale=locale_fallback)
+ self.assertTrue(nikola.nikola.is_valid_locale(sanitized_fallback))
+
+ def test_explicit_good_fallback(self):
+ locale_fallback = loc_22
+ sanitized_fallback = nikola.nikola.valid_locale_fallback(
+ desired_locale=locale_fallback)
+ self.assertEquals(sanitized_fallback, locale_fallback)
+
+ def test_explicit_bad_fallback(self):
+ locale_fallback = str('xyz')
+ sanitized_fallback = nikola.nikola.valid_locale_fallback(
+ desired_locale=locale_fallback)
+ self.assertTrue(nikola.nikola.is_valid_locale(sanitized_fallback))
+
+ def test_explicit_good_default(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ loc_11,
+ {},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertEquals(fallback, locale_fallback)
+ self.assertEquals(default, locale_default)
+
+ def test_explicit_bad_default(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ str('xyz'),
+ {},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertEquals(fallback, locale_fallback)
+ self.assertEquals(default, fallback)
+
+ def test_extra_locales_deleted(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ None,
+ {'@z': loc_22},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertTrue('@z' not in locales)
+
+ def test_explicit_good_locale_retained(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ loc_22,
+ {lang_11: loc_11},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertEquals(locales[lang_11], str(LOCALES[lang_11]))
+
+ def test_explicit_bad_locale_replaced_with_fallback(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ loc_11,
+ {lang_11: str('xyz')},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertEquals(locales['en'], locale_fallback)
+
+ def test_impicit_locale_when_default_locale_defined(self):
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_11,
+ loc_22,
+ {},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ self.assertEquals(locales['en'], locale_default)
+
+ def test_impicit_locale_when_default_locale_is_not_defined(self):
+ # legacy mode, compat v6.0.4 : guess locale from lang
+ locale_fallback, locale_default, LOCALES, translations = (
+ loc_22,
+ None,
+ {},
+ {lang_11: ''},
+ )
+ fallback, default, locales = nikola.nikola.sanitized_locales(
+ locale_fallback,
+ locale_default,
+ LOCALES,
+ translations)
+ if sys.platform == 'win32':
+ guess_locale_for_lang = nikola.nikola.guess_locale_from_lang_windows
+ else:
+ guess_locale_for_lang = nikola.nikola.guess_locale_from_lang_posix
+
+ self.assertEquals(locales[lang_11], guess_locale_for_lang(lang_11))
+
+
+class TestCalendarRelated(unittest.TestCase):
+ def test_type_of_month_name(self):
+ """validate assumption calendar month name is of type str
+
+ Yes, both in windows and linuxTravis, py 26, 27, 33
+ """
+ import calendar
+ if sys.version_info[0] == 3: # Python 3
+ with calendar.different_locale(loc_11):
+ s = calendar.month_name[1]
+ else: # Python 2
+ with calendar.TimeEncoding(loc_11):
+ s = calendar.month_name[1]
+ self.assertTrue(type(s) == str)
+
+
+class TestLocaleBorg(unittest.TestCase):
+ def test_initial_lang(self):
+ lang_11, loc_11 = LocaleSupportInTesting.langlocales['default']
+ lang_22, loc_22 = LocaleSupportInTesting.langlocales['other']
+
+ locales = {lang_11: loc_11, lang_22: loc_22}
+ initial_lang = lang_22
+ nikola.utils.LocaleBorg.initialize(locales, initial_lang)
+ self.assertEquals(initial_lang, nikola.utils.LocaleBorg().current_lang)
+
+ def test_remembers_last_lang(self):
+ lang_11, loc_11 = LocaleSupportInTesting.langlocales['default']
+ lang_22, loc_22 = LocaleSupportInTesting.langlocales['other']
+
+ locales = {lang_11: loc_11, lang_22: loc_22}
+ initial_lang = lang_22
+ nikola.utils.LocaleBorg.initialize(locales, initial_lang)
+
+ nikola.utils.LocaleBorg().set_locale(lang_11)
+ self.assertTrue(nikola.utils.LocaleBorg().current_lang, lang_11)
+
+ def test_services_ensure_initialization(self):
+ nikola.utils.LocaleBorg.reset()
+ self.assertRaises(Exception, nikola.utils.LocaleBorg)
+
+ def test_services_reject_dumb_wrong_call(self):
+ lang_11, loc_11 = LocaleSupportInTesting.langlocales['default']
+ nikola.utils.LocaleBorg.reset()
+ self.assertRaises(Exception, nikola.utils.LocaleBorg.set_locale, lang_11)
+ self.assertRaises(Exception, getattr, nikola.utils.LocaleBorg, 'current_lang')
+
+ def test_set_locale_raises_on_invalid_lang(self):
+ lang_11, loc_11 = LocaleSupportInTesting.langlocales['default']
+ lang_22, loc_22 = LocaleSupportInTesting.langlocales['other']
+
+ locales = {lang_11: loc_11, lang_22: loc_22}
+ initial_lang = lang_22
+ nikola.utils.LocaleBorg.initialize(locales, initial_lang)
+ self.assertRaises(KeyError, nikola.utils.LocaleBorg().set_locale, '@z')
+
+
+class TestTestPreconditions(unittest.TestCase):
+ """If this fails the other test in this module are mostly nonsense, and
+ probably same for tests of multilingual features.
+
+ Failure probably means the OS support for the failing locale is not
+ instaled or environmet variables NIKOLA_LOCALE_DEFAULT or
+ NIKOLA_LOCALE_OTHER with bad values.
+ """
+ def test_langlocale_default_availability(self):
+ msg = "META ERROR: The pair lang, locale : {0} {1} is invalid"
+ self.assertTrue(nikola.nikola.is_valid_locale(loc_11), msg.format(lang_11, loc_11))
+
+ def test_langlocale_other_availability(self):
+ msg = "META ERROR: The pair lang, locale : {0} {1} is invalid"
+ self.assertTrue(nikola.nikola.is_valid_locale(loc_22), msg.format(lang_22, loc_22))
diff --git a/tests/test_plugin_importing.py b/tests/test_plugin_importing.py
index 677dde8..5009f88 100644
--- a/tests/test_plugin_importing.py
+++ b/tests/test_plugin_importing.py
@@ -1,16 +1,16 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import
-from context import nikola # NOQA
+from .context import nikola # NOQA
import unittest
class ImportPluginsTest(unittest.TestCase):
def test_importing_command_import_wordpress(self):
- import nikola.plugins.command_import_wordpress # NOQA
+ import nikola.plugins.command.import_wordpress # NOQA
def test_importing_compile_rest(self):
- import nikola.plugins.compile_rest # NOQA
+ import nikola.plugins.compile.rest # NOQA
def test_importing_plugin_compile_markdown(self):
- import nikola.plugins.compile_markdown # NOQA
+ import nikola.plugins.compile.markdown # NOQA
diff --git a/tests/test_rss_feeds.py b/tests/test_rss_feeds.py
index 5b9b981..169e1e7 100644
--- a/tests/test_rss_feeds.py
+++ b/tests/test_rss_feeds.py
@@ -1,25 +1,37 @@
# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-import unittest
+from __future__ import unicode_literals, absolute_import
+from collections import defaultdict
+from io import StringIO
import os
import re
-from io import StringIO
+import unittest
import mock
-from context import nikola
+from .context import nikola # NOQA
from lxml import etree
+from .base import LocaleSupportInTesting
+
+
+fake_conf = defaultdict(str)
+fake_conf['TIMEZONE'] = None
+fake_conf['DEFAULT_LANG'] = 'en'
+fake_conf['TRANSLATIONS'] = {'en': ''}
+fake_conf['BASE_URL'] = 'http://some.blog/'
class RSSFeedTest(unittest.TestCase):
def setUp(self):
+ LocaleSupportInTesting.initialize_locales_for_testing('unilingual')
+
self.blog_url = "http://some.blog"
with mock.patch('nikola.post.get_meta',
mock.Mock(return_value=({'title': 'post title',
'slug': 'awesome_article',
'date': '2012-10-01 22:41',
+ 'author': None,
'tags': 'tags', 'link':
'link', 'description':
'description'}))):
@@ -29,14 +41,12 @@ class RSSFeedTest(unittest.TestCase):
mock.Mock(return_value='some long text')):
example_post = nikola.nikola.Post('source.file',
- 'cache',
+ fake_conf,
'blog_folder',
True,
{'en': ''},
- 'en',
- self.blog_url,
- 'unused message.',
- 'post.tmpl')
+ 'post.tmpl',
+ lambda *a: None)
opener_mock = mock.mock_open()
diff --git a/tests/test_rst_extensions.py b/tests/test_rst_compiler.py
index 845d6a7..c5db8dd 100644
--- a/tests/test_rst_extensions.py
+++ b/tests/test_rst_compiler.py
@@ -25,36 +25,134 @@ always unquoted.
"""
-from __future__ import unicode_literals
+from __future__ import unicode_literals, absolute_import
+import codecs
try:
from io import StringIO
except ImportError:
from StringIO import StringIO # NOQA
+import os
+import sys
+import tempfile
-from docutils.core import publish_parts
+import docutils
from lxml import html
-import mock
-
+from nose.plugins.skip import SkipTest
import unittest
-import nikola.plugins.compile_rest
-from nikola.utils import _reload
-from base import BaseTestCase
+from yapsy.PluginManager import PluginManager
+
+from nikola import utils
+import nikola.plugins.compile.rest
+from nikola.plugins.compile.rest import gist
+from nikola.plugins.compile.rest import vimeo
+import nikola.plugins.compile.rest.listing
+from nikola.plugins.compile.rest.doc import Plugin as DocPlugin
+from nikola.utils import _reload, STDERR_HANDLER
+from nikola.plugin_categories import (
+ Command,
+ Task,
+ LateTask,
+ TemplateSystem,
+ PageCompiler,
+ TaskMultiplier,
+ RestExtension,
+)
+from .base import BaseTestCase
+
+
+class FakePost(object):
+
+ def __init__(self, title, slug):
+ self._title = title
+ self._slug = slug
+ self._meta = {'slug': slug}
+
+ def title(self):
+ return self._title
+
+ def meta(self, key):
+ return self._meta[key]
+
+ def permalink(self):
+ return '/posts/' + self._slug
+
+
+class FakeSite(object):
+ def __init__(self):
+ self.template_system = self
+ self.config = {
+ 'DISABLED_PLUGINS': [],
+ 'EXTRA_PLUGINS': [],
+ 'DEFAULT_LANG': 'en',
+ }
+ self.EXTRA_PLUGINS = self.config['EXTRA_PLUGINS']
+ self.plugin_manager = PluginManager(categories_filter={
+ "Command": Command,
+ "Task": Task,
+ "LateTask": LateTask,
+ "TemplateSystem": TemplateSystem,
+ "PageCompiler": PageCompiler,
+ "TaskMultiplier": TaskMultiplier,
+ "RestExtension": RestExtension,
+ })
+ self.loghandlers = [STDERR_HANDLER]
+ self.plugin_manager.setPluginInfoExtension('plugin')
+ if sys.version_info[0] == 3:
+ places = [
+ os.path.join(os.path.dirname(utils.__file__), 'plugins'),
+ ]
+ else:
+ places = [
+ os.path.join(os.path.dirname(utils.__file__), utils.sys_encode('plugins')),
+ ]
+ self.plugin_manager.setPluginPlaces(places)
+ self.plugin_manager.collectPlugins()
+
+ self.timeline = [
+ FakePost(title='Fake post',
+ slug='fake-post')
+ ]
+
+ def render_template(self, name, _, context):
+ return('<img src="IMG.jpg">')
class ReSTExtensionTestCase(BaseTestCase):
""" Base class for testing ReST extensions """
- sample = None
+ sample = 'foo'
+ deps = None
def setUp(self):
+ self.compiler = nikola.plugins.compile.rest.CompileRest()
+ self.compiler.set_site(FakeSite())
+ return super(ReSTExtensionTestCase, self).setUp()
+
+ def basic_test(self):
""" Parse cls.sample into a HTML document tree """
- super(ReSTExtensionTestCase, self).setUp()
self.setHtmlFromRst(self.sample)
def setHtmlFromRst(self, rst):
""" Create html output from rst string """
- self.html = publish_parts(rst, writer_name="html")["body"]
+ tmpdir = tempfile.mkdtemp()
+ inf = os.path.join(tmpdir, 'inf')
+ outf = os.path.join(tmpdir, 'outf')
+ depf = os.path.join(tmpdir, 'outf.dep')
+ with codecs.open(inf, 'wb+', 'utf8') as f:
+ f.write(rst)
+ self.html = self.compiler.compile_html(inf, outf)
+ with codecs.open(outf, 'r', 'utf8') as f:
+ self.html = f.read()
+ os.unlink(inf)
+ os.unlink(outf)
+ if os.path.isfile(depf):
+ with codecs.open(depf, 'r', 'utf8') as f:
+ self.assertEqual(self.deps, f.read())
+ os.unlink(depf)
+ else:
+ self.assertEqual(self.deps, None)
+ os.rmdir(tmpdir)
self.html_doc = html.parse(StringIO(self.html))
def assertHTMLContains(self, element, attributes=None, text=None):
@@ -65,7 +163,7 @@ class ReSTExtensionTestCase(BaseTestCase):
try:
tag = next(self.html_doc.iter(element))
except StopIteration:
- raise Exception("<{}> not in {}".format(element, self.html))
+ raise Exception("<{0}> not in {1}".format(element, self.html))
else:
if attributes:
arg_attrs = set(attributes.items())
@@ -81,29 +179,42 @@ class ReSTExtensionTestCaseTestCase(ReSTExtensionTestCase):
sample = '.. raw:: html\n\n <iframe src="foo" height="bar">spam</iframe>'
def test_test(self):
+ self.basic_test()
self.assertHTMLContains("iframe", attributes={"src": "foo"},
text="spam")
self.assertRaises(Exception, self.assertHTMLContains, "eggs", {})
+class MathTestCase(ReSTExtensionTestCase):
+ sample = ':math:`e^{ix} = \cos x + i\sin x`'
+
+ def test_mathjax(self):
+ """ Test that math is outputting MathJax."""
+ self.basic_test()
+ self.assertHTMLContains("span", attributes={"class": "math"},
+ text="\(e^{ix} = \cos x + i\sin x\)")
+
+
class GistTestCase(ReSTExtensionTestCase):
""" Test GitHubGist.
We will replace get_raw_gist() and get_raw_gist_with_filename()
monkeypatching the GitHubGist class for avoiding network dependency
"""
- gist_type = nikola.plugins.compile_rest.GitHubGist
+ gist_type = gist.GitHubGist
sample = '.. gist:: fake_id\n :file: spam.py'
sample_without_filename = '.. gist:: fake_id2'
def setUp(self):
""" Patch GitHubGist for avoiding network dependency """
+ super(GistTestCase, self).setUp()
self.gist_type.get_raw_gist_with_filename = lambda *_: 'raw_gist_file'
self.gist_type.get_raw_gist = lambda *_: "raw_gist"
- _reload(nikola.plugins.compile_rest)
+ _reload(nikola.plugins.compile.rest)
def test_gist(self):
""" Test the gist directive with filename """
+ raise SkipTest
self.setHtmlFromRst(self.sample)
output = 'https://gist.github.com/fake_id.js?file=spam.py'
self.assertHTMLContains("script", attributes={"src": output})
@@ -111,6 +222,7 @@ class GistTestCase(ReSTExtensionTestCase):
def test_gist_without_filename(self):
""" Test the gist directive without filename """
+ raise SkipTest
self.setHtmlFromRst(self.sample_without_filename)
output = 'https://gist.github.com/fake_id2.js'
self.assertHTMLContains("script", attributes={"src": output})
@@ -126,6 +238,7 @@ class GistIntegrationTestCase(ReSTExtensionTestCase):
def test_gist_integration(self):
""" Fetch contents of the gist from GH and render in a noscript tag """
+ self.basic_test()
text = ('Be alone, that is the secret of invention: be alone, that is'
' when ideas are born. -- Nikola Tesla')
self.assertHTMLContains('pre', text=text)
@@ -138,6 +251,7 @@ class SlidesTestCase(ReSTExtensionTestCase):
def test_slides(self):
""" Test the slides js generation and img tag creation """
+ self.basic_test()
self.assertHTMLContains("img", attributes={"src": "IMG.jpg"})
@@ -148,6 +262,7 @@ class SoundCloudTestCase(ReSTExtensionTestCase):
def test_soundcloud(self):
""" Test SoundCloud iframe tag generation """
+ self.basic_test()
self.assertHTMLContains("iframe",
attributes={"src": ("https://w.soundcloud.com"
"/player/?url=http://"
@@ -166,12 +281,13 @@ class VimeoTestCase(ReSTExtensionTestCase):
def setUp(self):
""" Disable query of the vimeo api over the wire """
- nikola.plugins.compile_rest.Vimeo.request_size = False
+ vimeo.Vimeo.request_size = False
super(VimeoTestCase, self).setUp()
- _reload(nikola.plugins.compile_rest)
+ _reload(nikola.plugins.compile.rest)
def test_vimeo(self):
""" Test Vimeo iframe tag generation """
+ self.basic_test()
self.assertHTMLContains("iframe",
attributes={"src": ("http://player.vimeo.com/"
"video/VID"),
@@ -185,6 +301,7 @@ class YoutubeTestCase(ReSTExtensionTestCase):
def test_youtube(self):
""" Test Youtube iframe tag generation """
+ self.basic_test()
self.assertHTMLContains("iframe",
attributes={"src": ("http://www.youtube.com/"
"embed/YID?rel=0&hd=1&"
@@ -195,28 +312,57 @@ class YoutubeTestCase(ReSTExtensionTestCase):
class ListingTestCase(ReSTExtensionTestCase):
""" Listing test case and CodeBlock alias tests """
- sample = '.. listing:: nikola.py python'
+ deps = None
+ sample1 = '.. listing:: nikola.py python\n\n'
sample2 = '.. code-block:: python\n\n import antigravity'
sample3 = '.. sourcecode:: python\n\n import antigravity'
- opener_mock = mock.mock_open(read_data="import antigravity\n")
- opener_mock.return_value.readlines.return_value = "import antigravity\n"
-
- def setUp(self):
- """ Inject a mock open function for not generating a test site """
- self.f = StringIO("import antigravity\n")
- #_reload(nikola.plugins.compile_rest)
-
- def test_listing(self):
- """ Test that we can render a file object contents without errors """
- with mock.patch("nikola.plugins.compile_rest.listing.codecs_open", self.opener_mock, create=True):
- self.setHtmlFromRst(self.sample)
+ #def test_listing(self):
+ ##""" Test that we can render a file object contents without errors """
+ ##with cd(os.path.dirname(__file__)):
+ #self.deps = 'listings/nikola.py'
+ #self.setHtmlFromRst(self.sample1)
def test_codeblock_alias(self):
""" Test CodeBlock aliases """
- with mock.patch("nikola.plugins.compile_rest.listing.codecs_open", self.opener_mock, create=True):
- self.setHtmlFromRst(self.sample2)
- self.setHtmlFromRst(self.sample3)
+ self.deps = None
+ self.setHtmlFromRst(self.sample2)
+ self.setHtmlFromRst(self.sample3)
+
+
+class DocTestCase(ReSTExtensionTestCase):
+ """ Ref role test case """
+
+ sample = 'Sample for testing my :doc:`doesnt-exist-post`'
+ sample1 = 'Sample for testing my :doc:`fake-post`'
+ sample2 = 'Sample for testing my :doc:`titled post <fake-post>`'
+
+ def setUp(self):
+ # Initialize plugin, register role
+ self.plugin = DocPlugin()
+ self.plugin.set_site(FakeSite())
+ # Hack to fix leaked state from integration tests
+ try:
+ f = docutils.parsers.rst.roles.role('doc', None, None, None)[0]
+ f.site = FakeSite()
+ except AttributeError:
+ pass
+ return super(DocTestCase, self).setUp()
+
+ def test_doc_doesnt_exist(self):
+ self.assertRaises(Exception, self.assertHTMLContains, 'anything', {})
+
+ def test_doc(self):
+ self.setHtmlFromRst(self.sample1)
+ self.assertHTMLContains('a',
+ text='Fake post',
+ attributes={'href': '/posts/fake-post'})
+
+ def test_doc_titled(self):
+ self.setHtmlFromRst(self.sample2)
+ self.assertHTMLContains('a',
+ text='titled post',
+ attributes={'href': '/posts/fake-post'})
if __name__ == "__main__":
diff --git a/tests/test_scheduling.py b/tests/test_scheduling.py
new file mode 100644
index 0000000..264d1c5
--- /dev/null
+++ b/tests/test_scheduling.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals, absolute_import
+from .base import BaseTestCase
+import datetime
+from nose.plugins.skip import SkipTest
+try:
+ from freezegun import freeze_time
+ _freeze_time = True
+except ImportError:
+ _freeze_time = False
+ freeze_time = lambda x: lambda y: y
+
+FMT = '%Y/%m/%d %H:%M:%S'
+NOW = '2013/08/22 10:00:00' # Thursday
+TODAY = datetime.datetime.strptime(NOW, FMT)
+RULE_TH = 'RRULE:FREQ=WEEKLY;BYDAY=TH'
+RULE_FR = 'RRULE:FREQ=WEEKLY;BYDAY=FR'
+
+
+class TestScheduling(BaseTestCase):
+
+ @classmethod
+ def setUp(self):
+ if not _freeze_time:
+ raise SkipTest('freezegun not installed')
+
+ @freeze_time(NOW)
+ def test_get_date(self):
+ from nikola.plugins.command.new_post import get_date
+
+ #### NOW does not match rule #########################################
+ ## No last date
+ expected = TODAY.replace(day=23).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_FR))
+ self.assertEqual(expected, get_date(True, RULE_FR, force_today=True))
+
+ ## Last date in the past; doesn't match rule
+ date = TODAY.replace(hour=7)
+ expected = TODAY.replace(day=23, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_FR, date))
+ self.assertEqual(expected, get_date(True, RULE_FR, date, True))
+
+ ## Last date in the future; doesn't match rule
+ date = TODAY.replace(day=24, hour=7)
+ expected = TODAY.replace(day=30, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_FR, date))
+ self.assertEqual(expected, get_date(True, RULE_FR, date, True))
+
+ ## Last date in the past; matches rule
+ date = TODAY.replace(day=16, hour=8)
+ expected = TODAY.replace(day=23, hour=8).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_FR, date))
+ self.assertEqual(expected, get_date(True, RULE_FR, date, True))
+
+ ## Last date in the future; matches rule
+ date = TODAY.replace(day=23, hour=18)
+ expected = TODAY.replace(day=30, hour=18).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_FR, date))
+ self.assertEqual(expected, get_date(True, RULE_FR, date, True))
+
+ #### NOW matches rule ################################################
+ ## Not scheduling should return NOW
+ self.assertEqual(NOW, get_date(False, RULE_TH))
+ ## No last date
+ self.assertEqual(NOW, get_date(True, RULE_TH))
+ self.assertEqual(NOW, get_date(True, RULE_TH, force_today=True))
+
+ ## Last date in the past; doesn't match rule
+ ### Corresponding time has already passed, today
+ date = TODAY.replace(day=21, hour=7)
+ expected = TODAY.replace(day=29, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ expected = TODAY.replace(day=22, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+ ### Corresponding time has not passed today
+ date = TODAY.replace(day=21, hour=18)
+ expected = TODAY.replace(day=22, hour=18).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+
+ ## Last date in the future; doesn't match rule
+ ### Corresponding time has already passed, today
+ date = TODAY.replace(day=24, hour=7)
+ expected = TODAY.replace(day=29, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+ ### Corresponding time has not passed today
+ date = TODAY.replace(day=24, hour=18)
+ expected = TODAY.replace(day=29, hour=18).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+
+ ## Last date in the past; matches rule
+ ### Corresponding time has already passed, today
+ date = TODAY.replace(day=15, hour=7)
+ expected = TODAY.replace(day=29, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ expected = TODAY.replace(day=22, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+ ### Corresponding time has already passed, today; rule specifies HOUR
+ date = TODAY.replace(day=15, hour=7)
+ expected = TODAY.replace(day=29, hour=9).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH + ';BYHOUR=9', date))
+ expected = TODAY.replace(day=22, hour=9).strftime(FMT)
+ self.assertEqual(expected,
+ get_date(True, RULE_TH + ';BYHOUR=9', date, True))
+ ### Corresponding time has not passed today
+ date = TODAY.replace(day=15, hour=18)
+ expected = TODAY.replace(day=22, hour=18).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+
+ ## Last date in the future; matches rule
+ ### Corresponding time has already passed, today
+ date = TODAY.replace(day=29, hour=7)
+ expected = TODAY.replace(day=5, month=9, hour=7).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ ### Corresponding time has not passed today
+ date = TODAY.replace(day=22, hour=18)
+ expected = TODAY.replace(day=29, hour=18).strftime(FMT)
+ self.assertEqual(expected, get_date(True, RULE_TH, date))
+ self.assertEqual(expected, get_date(True, RULE_TH, date, True))
+
+if __name__ == '__main__':
+ import unittest
+ unittest.main()
diff --git a/tests/wordpress_export_example.xml b/tests/wordpress_export_example.xml
index e697a5b..5fd0a90 100644
--- a/tests/wordpress_export_example.xml
+++ b/tests/wordpress_export_example.xml
@@ -230,5 +230,62 @@ A listing with another listing inside.
</wp:postmeta>
</item>
-</channel>
+ <item>
+ <title>Screenshot - 2012-12-19</title>
+ <link>http://some.blog/2012/12/wintermodus/2012-12-19-1355925145_1024x600_scrot/</link>
+ <pubDate>Wed, 19 Dec 2012 13:53:19 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/wp-content/uploads/2012/12/2012-12-19-355925145_1024x600_scrot.png</guid>
+ <description></description>
+ <content:encoded><![CDATA[]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>2271</wp:post_id>
+ <wp:post_date>2012-12-19 14:53:19</wp:post_date>
+ <wp:post_date_gmt>2012-12-19 13:53:19</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>2012-12-19-1355925145_1024x600_scrot</wp:post_name>
+ <wp:status>inherit</wp:status>
+ <wp:post_parent>2270</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>attachment</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <wp:attachment_url>http://some.blog/wp-content/uploads/2012/12/2012-12-19-355925145_1024x600_scrot.png</wp:attachment_url>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attached_file</wp:meta_key>
+ <wp:meta_value><![CDATA[2012/12/2012-12-19-1355925145_1024x600_scrot.png]]></wp:meta_value>
+ </wp:postmeta>
+ <wp:postmeta>
+ <wp:meta_key>_wp_attachment_metadata</wp:meta_key>
+ <wp:meta_value><![CDATA[a:5:{s:5:"width";i:1024;s:6:"height";i:600;s:4:"file";s:48:"2012/12/2012-12-19-1355925145_1024x600_scrot.png";s:5:"sizes";a:9:{s:9:"thumbnail";a:4:{s:4:"file";s:48:"2012-12-19-1355925145_1024x600_scrot-150x150.png";s:5:"width";i:150;s:6:"height";i:150;s:9:"mime-type";s:9:"image/png";}s:6:"medium";a:4:{s:4:"file";s:48:"2012-12-19-1355925145_1024x600_scrot-300x175.png";s:5:"width";i:300;s:6:"height";i:175;s:9:"mime-type";s:9:"image/png";}s:12:"mosaic-thumb";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-96x96.png";s:5:"width";i:96;s:6:"height";i:96;s:9:"mime-type";s:9:"image/png";}s:13:"gallery-thumb";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-96x96.png";s:5:"width";i:96;s:6:"height";i:96;s:9:"mime-type";s:9:"image/png";}s:9:"widget-24";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-24x24.png";s:5:"width";i:24;s:6:"height";i:24;s:9:"mime-type";s:9:"image/png";}s:9:"widget-32";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-36x36.png";s:5:"width";i:36;s:6:"height";i:36;s:9:"mime-type";s:9:"image/png";}s:9:"widget-48";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-48x48.png";s:5:"width";i:48;s:6:"height";i:48;s:9:"mime-type";s:9:"image/png";}s:9:"widget-64";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-64x64.png";s:5:"width";i:64;s:6:"height";i:64;s:9:"mime-type";s:9:"image/png";}s:9:"widget-96";a:4:{s:4:"file";s:46:"2012-12-19-1355925145_1024x600_scrot-96x96.png";s:5:"width";i:96;s:6:"height";i:96;s:9:"mime-type";s:9:"image/png";}}s:10:"image_meta";a:10:{s:8:"aperture";i:0;s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";i:0;s:9:"copyright";s:0:"";s:12:"focal_length";i:0;s:3:"iso";i:0;s:13:"shutter_speed";i:0;s:5:"title";s:0:"";}}]]></wp:meta_value>
+ </wp:postmeta>
+ </item>
+
+ <item>
+ <title>Image Link Rewriting</title>
+ <link>http://some.blog/2012/12/wintermodus/</link>
+ <pubDate>Wed, 19 Dec 2012 13:55:10 +0000</pubDate>
+ <dc:creator>Niko</dc:creator>
+ <guid isPermaLink="false">http://some.blog/?p=2270</guid>
+ <description></description>
+ <content:encoded><![CDATA[Some image upload. The links to this and the src of the img-tag should be rewritten correctly.
+
+ <a href="http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot.png"><img class="aligncenter size-medium wp-image-2271" alt="Netbook Screenshot - 2012-12-19" src="http://some.blog/wp-content/uploads/2012/12/2012-12-19-1355925145_1024x600_scrot-300x175.ng" width="300" height="175" /></a>]]></content:encoded>
+ <excerpt:encoded><![CDATA[]]></excerpt:encoded>
+ <wp:post_id>2270</wp:post_id>
+ <wp:post_date>2012-12-19 14:55:10</wp:post_date>
+ <wp:post_date_gmt>2012-12-19 13:55:10</wp:post_date_gmt>
+ <wp:comment_status>open</wp:comment_status>
+ <wp:ping_status>open</wp:ping_status>
+ <wp:post_name>image-link-rewriting</wp:post_name>
+ <wp:status>publish</wp:status>
+ <wp:post_parent>0</wp:post_parent>
+ <wp:menu_order>0</wp:menu_order>
+ <wp:post_type>post</wp:post_type>
+ <wp:post_password></wp:post_password>
+ <wp:is_sticky>0</wp:is_sticky>
+ <category domain="category" nicename="linux"><![CDATA[Linux]]></category>
+ </item>
+ </channel>
</rss>