Fix Windows tests

* Unskip passable tests
* Fix broken tests
This commit is contained in:
Deniz Turgut 2020-04-28 23:29:44 +03:00
commit 2e482b207b
No known key found for this signature in database
GPG key ID: 87B7168D7AB3ED2F
10 changed files with 58 additions and 41 deletions

View file

@ -696,7 +696,7 @@ def path_metadata(full_path, source_path, settings=None):
# Enforce a trailing slash when checking for parent directories. # Enforce a trailing slash when checking for parent directories.
# This prevents false positives when one file or directory's name # This prevents false positives when one file or directory's name
# is a prefix of another's. # is a prefix of another's.
dirpath = os.path.join(path, '') dirpath = posixize_path(os.path.join(path, ''))
if source_path == path or source_path.startswith(dirpath): if source_path == path or source_path.startswith(dirpath):
metadata.update(meta) metadata.update(meta)

View file

@ -160,6 +160,19 @@ def locale_available(locale_):
return True return True
def can_symlink():
res = True
try:
with temporary_folder() as f:
os.symlink(
f,
os.path.join(f, 'symlink')
)
except OSError:
res = False
return res
def get_settings(**kwargs): def get_settings(**kwargs):
"""Provide tweaked setting dictionaries for testing """Provide tweaked setting dictionaries for testing

View file

@ -1,5 +1,6 @@
import locale import locale
import os import os
import sys
from shutil import copy, rmtree from shutil import copy, rmtree
from tempfile import mkdtemp from tempfile import mkdtemp
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -7,7 +8,8 @@ from unittest.mock import MagicMock
from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator, from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
PelicanTemplateNotFound, StaticGenerator, PelicanTemplateNotFound, StaticGenerator,
TemplatePagesGenerator) TemplatePagesGenerator)
from pelican.tests.support import get_context, get_settings, unittest from pelican.tests.support import (can_symlink, get_context, get_settings,
unittest)
from pelican.writers import Writer from pelican.writers import Writer
@ -1081,6 +1083,7 @@ class TestStaticGenerator(unittest.TestCase):
self.generator.generate_output(None) self.generator.generate_output(None)
self.assertTrue(os.path.samefile(self.startfile, self.endfile)) self.assertTrue(os.path.samefile(self.startfile, self.endfile))
@unittest.skipUnless(can_symlink(), 'No symlink privilege')
def test_can_symlink_when_hardlink_not_possible(self): def test_can_symlink_when_hardlink_not_possible(self):
self.settings['STATIC_CREATE_LINKS'] = True self.settings['STATIC_CREATE_LINKS'] = True
with open(self.startfile, "w") as f: with open(self.startfile, "w") as f:
@ -1088,40 +1091,29 @@ class TestStaticGenerator(unittest.TestCase):
os.mkdir(os.path.join(self.temp_output, "static")) os.mkdir(os.path.join(self.temp_output, "static"))
self.generator.fallback_to_symlinks = True self.generator.fallback_to_symlinks = True
self.generator.generate_context() self.generator.generate_context()
try: self.generator.generate_output(None)
self.generator.generate_output(None)
except OSError as e:
# On Windows, possibly others, due to not holding symbolic link
# privilege
self.skipTest(e)
self.assertTrue(os.path.islink(self.endfile)) self.assertTrue(os.path.islink(self.endfile))
@unittest.skipUnless(can_symlink(), 'No symlink privilege')
def test_existing_symlink_is_considered_up_to_date(self): def test_existing_symlink_is_considered_up_to_date(self):
self.settings['STATIC_CREATE_LINKS'] = True self.settings['STATIC_CREATE_LINKS'] = True
with open(self.startfile, "w") as f: with open(self.startfile, "w") as f:
f.write("staticcontent") f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static")) os.mkdir(os.path.join(self.temp_output, "static"))
try: os.symlink(self.startfile, self.endfile)
os.symlink(self.startfile, self.endfile)
except OSError as e:
# On Windows, possibly others
self.skipTest(e)
staticfile = MagicMock() staticfile = MagicMock()
staticfile.source_path = self.startfile staticfile.source_path = self.startfile
staticfile.save_as = self.endfile staticfile.save_as = self.endfile
requires_update = self.generator._file_update_required(staticfile) requires_update = self.generator._file_update_required(staticfile)
self.assertFalse(requires_update) self.assertFalse(requires_update)
@unittest.skipUnless(can_symlink(), 'No symlink privilege')
def test_invalid_symlink_is_overwritten(self): def test_invalid_symlink_is_overwritten(self):
self.settings['STATIC_CREATE_LINKS'] = True self.settings['STATIC_CREATE_LINKS'] = True
with open(self.startfile, "w") as f: with open(self.startfile, "w") as f:
f.write("staticcontent") f.write("staticcontent")
os.mkdir(os.path.join(self.temp_output, "static")) os.mkdir(os.path.join(self.temp_output, "static"))
try: os.symlink("invalid", self.endfile)
os.symlink("invalid", self.endfile)
except OSError as e:
# On Windows, possibly others
self.skipTest(e)
staticfile = MagicMock() staticfile = MagicMock()
staticfile.source_path = self.startfile staticfile.source_path = self.startfile
staticfile.save_as = self.endfile staticfile.save_as = self.endfile
@ -1131,8 +1123,18 @@ class TestStaticGenerator(unittest.TestCase):
self.generator.generate_context() self.generator.generate_context()
self.generator.generate_output(None) self.generator.generate_output(None)
self.assertTrue(os.path.islink(self.endfile)) self.assertTrue(os.path.islink(self.endfile))
self.assertEqual(os.path.realpath(self.endfile),
os.path.realpath(self.startfile)) # os.path.realpath is broken on Windows before python3.8 for symlinks.
# This is a (ugly) workaround.
# see: https://bugs.python.org/issue9949
if os.name == 'nt' and sys.version_info < (3, 8):
def get_real_path(path):
return os.readlink(path) if os.path.islink(path) else path
else:
get_real_path = os.path.realpath
self.assertEqual(get_real_path(self.endfile),
get_real_path(self.startfile))
def test_delete_existing_file_before_mkdir(self): def test_delete_existing_file_before_mkdir(self):
with open(self.startfile, "w") as f: with open(self.startfile, "w") as f:

View file

@ -1,6 +1,7 @@
import locale import locale
import os import os
import re import re
from posixpath import join as posix_join
from pelican.settings import DEFAULT_CONFIG from pelican.settings import DEFAULT_CONFIG
from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder, from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder,
@ -448,5 +449,5 @@ class TestWordpressXMLAttachements(unittest.TestCase):
self.assertEqual(1, len(locations)) self.assertEqual(1, len(locations))
directory = locations[0] directory = locations[0]
self.assertTrue( self.assertTrue(
directory.endswith(os.path.join('content', 'article.rst')), directory.endswith(posix_join('content', 'article.rst')),
directory) directory)

View file

@ -25,11 +25,10 @@ class TestServer(unittest.TestCase):
os.chdir(self.temp_output) os.chdir(self.temp_output)
def tearDown(self): def tearDown(self):
rmtree(self.temp_output)
os.chdir(self.old_cwd) os.chdir(self.old_cwd)
rmtree(self.temp_output)
def test_get_path_that_exists(self): def test_get_path_that_exists(self):
handler = ComplexHTTPRequestHandler(MockRequest(), ('0.0.0.0', 8888), handler = ComplexHTTPRequestHandler(MockRequest(), ('0.0.0.0', 8888),
self.server) self.server)
handler.base_path = self.temp_output handler.base_path = self.temp_output

View file

@ -136,6 +136,8 @@ class TestSettingsConfiguration(unittest.TestCase):
settings['ARTICLE_DIR'] settings['ARTICLE_DIR']
settings['PAGE_DIR'] settings['PAGE_DIR']
# locale.getdefaultlocale() is broken on Windows
# See: https://bugs.python.org/issue37945
@unittest.skipIf(platform == 'win32', "Doesn't work on Windows") @unittest.skipIf(platform == 'win32', "Doesn't work on Windows")
def test_default_encoding(self): def test_default_encoding(self):
# Test that the default locale is set if not specified in settings # Test that the default locale is set if not specified in settings

View file

@ -757,35 +757,32 @@ class TestDateFormatter(unittest.TestCase):
class TestSanitisedJoin(unittest.TestCase): class TestSanitisedJoin(unittest.TestCase):
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_detect_parent_breakout(self): def test_detect_parent_breakout(self):
with self.assertRaisesRegex( with self.assertRaisesRegex(
RuntimeError, RuntimeError,
"Attempted to break out of output directory to /foo/test"): "Attempted to break out of output directory to "
"(.*?:)?/foo/test"): # (.*?:)? accounts for Windows root
utils.sanitised_join( utils.sanitised_join(
"/foo/bar", "/foo/bar",
"../test" "../test"
) )
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_detect_root_breakout(self): def test_detect_root_breakout(self):
with self.assertRaisesRegex( with self.assertRaisesRegex(
RuntimeError, RuntimeError,
"Attempted to break out of output directory to /test"): "Attempted to break out of output directory to "
"(.*?:)?/test"): # (.*?:)? accounts for Windows root
utils.sanitised_join( utils.sanitised_join(
"/foo/bar", "/foo/bar",
"/test" "/test"
) )
@unittest.skipIf(platform == 'win32',
"Different filesystem root on Windows")
def test_pass_deep_subpaths(self): def test_pass_deep_subpaths(self):
self.assertEqual( self.assertEqual(
utils.sanitised_join( utils.sanitised_join(
"/foo/bar", "/foo/bar",
"test" "test"
), ),
os.path.join("/foo/bar", "test") utils.posixize_path(
os.path.abspath(os.path.join("/foo/bar", "test")))
) )

View file

@ -728,8 +728,9 @@ def download_attachments(output_path, urls):
# Generate percent-encoded URL # Generate percent-encoded URL
scheme, netloc, path, query, fragment = urlsplit(url) scheme, netloc, path, query, fragment = urlsplit(url)
path = quote(path) if scheme != 'file':
url = urlunsplit((scheme, netloc, path, query, fragment)) path = quote(path)
url = urlunsplit((scheme, netloc, path, query, fragment))
if not os.path.exists(full_path): if not os.path.exists(full_path):
os.makedirs(full_path) os.makedirs(full_path)

View file

@ -27,8 +27,10 @@ logger = logging.getLogger(__name__)
def sanitised_join(base_directory, *parts): def sanitised_join(base_directory, *parts):
joined = os.path.abspath(os.path.join(base_directory, *parts)) joined = posixize_path(
if not joined.startswith(os.path.abspath(base_directory)): os.path.abspath(os.path.join(base_directory, *parts)))
base = posixize_path(os.path.abspath(base_directory))
if not joined.startswith(base):
raise RuntimeError( raise RuntimeError(
"Attempted to break out of output directory to {}".format( "Attempted to break out of output directory to {}".format(
joined joined
@ -391,10 +393,9 @@ def get_relative_path(path):
def path_to_url(path): def path_to_url(path):
"""Return the URL corresponding to a given path.""" """Return the URL corresponding to a given path."""
if os.sep == '/': if path is not None:
return path path = posixize_path(path)
else: return path
return '/'.join(split_all(path))
def posixize_path(rel_path): def posixize_path(rel_path):

View file

@ -1,5 +1,6 @@
import logging import logging
import os import os
from posixpath import join as posix_join
from urllib.parse import urljoin from urllib.parse import urljoin
from feedgenerator import Atom1Feed, Rss201rev2Feed, get_tag_uri from feedgenerator import Atom1Feed, Rss201rev2Feed, get_tag_uri
@ -25,7 +26,7 @@ class Writer:
# See Content._link_replacer for details # See Content._link_replacer for details
if self.settings['RELATIVE_URLS']: if self.settings['RELATIVE_URLS']:
self.urljoiner = os.path.join self.urljoiner = posix_join
else: else:
self.urljoiner = lambda base, url: urljoin( self.urljoiner = lambda base, url: urljoin(
base if base.endswith('/') else base + '/', url) base if base.endswith('/') else base + '/', url)