diff --git a/pyproject.toml b/pyproject.toml index f4fc33fec..eae5943ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,10 +3,11 @@ requires = ["setuptools"] build-backend = "setuptools.build_meta" [tool.pytest.ini_options] -python_files = 'test_*.py' -testpaths = 'test' # space separated list of paths from root e.g test tests doc/testing -addopts = '--cov=git --cov-report=term --disable-warnings' -filterwarnings = 'ignore::DeprecationWarning' +addopts = "--cov=git --cov-report=term --disable-warnings" +filterwarnings = "ignore::DeprecationWarning" +python_files = "test_*.py" +tmp_path_retention_policy = "failed" +testpaths = "test" # Space separated list of paths from root e.g test tests doc/testing. # --cov coverage # --cov-report term # send report to terminal term-missing -> terminal with line numbers html xml # --cov-report term-missing # to terminal with line numbers @@ -29,7 +30,7 @@ show_error_codes = true implicit_reexport = true # strict = true -# TODO: remove when 'gitdb' is fully annotated +# TODO: Remove when 'gitdb' is fully annotated. exclude = ["^git/ext/gitdb"] [[tool.mypy.overrides]] module = "gitdb.*" @@ -44,5 +45,5 @@ omit = ["*/git/ext/*"] [tool.black] line-length = 120 -target-version = ['py37'] +target-version = ["py37"] extend-exclude = "git/ext/gitdb" diff --git a/test-requirements.txt b/test-requirements.txt index a69181be1..7cfb977a1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,8 +4,8 @@ ddt >= 1.1.1, != 1.4.3 mock ; python_version < "3.8" mypy pre-commit -pytest +pytest >= 7.3.1 pytest-cov pytest-instafail -pytest-subtests +pytest-mock pytest-sugar diff --git a/test/test_util.py b/test/test_util.py index f75231c98..d345247b1 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -1,11 +1,10 @@ -# test_utils.py +# test_util.py # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors # # This module is part of GitPython and is released under # the BSD License: https://opensource.org/license/bsd-3-clause/ import ast -import contextlib from datetime import datetime import os import pathlib @@ -15,7 +14,7 @@ import sys import tempfile import time -from unittest import SkipTest, mock, skipIf, skipUnless +from unittest import SkipTest, mock import ddt import pytest @@ -44,140 +43,142 @@ from test.lib import TestBase, with_rw_repo -class _Member: - """A member of an IterableList.""" +@pytest.fixture +def permission_error_tmpdir(tmp_path): + """Fixture to test permissions errors in situations where they are not overcome.""" + td = tmp_path / "testdir" + td.mkdir() + (td / "x").write_bytes(b"") - __slots__ = ("name",) + # Set up PermissionError on Windows, where we can't delete read-only files. + (td / "x").chmod(stat.S_IRUSR) - def __init__(self, name): - self.name = name + # Set up PermissionError on Unix, where we can't delete files in read-only directories. + td.chmod(stat.S_IRUSR | stat.S_IXUSR) - def __repr__(self): - return f"{type(self).__name__}({self.name!r})" + yield td -@contextlib.contextmanager -def _tmpdir_to_force_permission_error(): - """Context manager to test permission errors in situations where they are not overcome.""" - if sys.platform == "cygwin": - raise SkipTest("Cygwin can't set the permissions that make the test meaningful.") - if sys.version_info < (3, 8): - raise SkipTest("In 3.7, TemporaryDirectory doesn't clean up after weird permissions.") +class TestRmtree: + """Tests for :func:`git.util.rmtree`.""" - with tempfile.TemporaryDirectory() as parent: - td = pathlib.Path(parent, "testdir") - td.mkdir() - (td / "x").write_bytes(b"") - (td / "x").chmod(stat.S_IRUSR) # Set up PermissionError on Windows. - td.chmod(stat.S_IRUSR | stat.S_IXUSR) # Set up PermissionError on Unix. - yield td + def test_deletes_nested_dir_with_files(self, tmp_path): + td = tmp_path / "testdir" + for d in td, td / "q", td / "s": + d.mkdir() + for f in ( + td / "p", + td / "q" / "w", + td / "q" / "x", + td / "r", + td / "s" / "y", + td / "s" / "z", + ): + f.write_bytes(b"") -@contextlib.contextmanager -def _tmpdir_for_file_not_found(): - """Context manager to test errors deleting a directory that are not due to permissions.""" - with tempfile.TemporaryDirectory() as parent: - yield pathlib.Path(parent, "testdir") # It is deliberately never created. + try: + rmtree(td) + except SkipTest as ex: + pytest.fail(f"rmtree unexpectedly attempts skip: {ex!r}") + assert not td.exists() -@ddt.ddt -class TestUtils(TestBase): - def test_rmtree_deletes_nested_dir_with_files(self): - with tempfile.TemporaryDirectory() as parent: - td = pathlib.Path(parent, "testdir") - for d in td, td / "q", td / "s": - d.mkdir() - for f in ( - td / "p", - td / "q" / "w", - td / "q" / "x", - td / "r", - td / "s" / "y", - td / "s" / "z", - ): - f.write_bytes(b"") + @pytest.mark.skipif( + sys.platform == "cygwin", + reason="Cygwin can't set the permissions that make the test meaningful.", + ) + def test_deletes_dir_with_readonly_files(self, tmp_path): + # Automatically works on Unix, but requires special handling on Windows. + # Not to be confused with what permission_error_tmpdir sets up (see below). - try: - rmtree(td) - except SkipTest as ex: - self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") + td = tmp_path / "testdir" - self.assertFalse(td.exists()) + for d in td, td / "sub": + d.mkdir() + for f in td / "x", td / "sub" / "y": + f.write_bytes(b"") + f.chmod(0) - @skipIf( + try: + rmtree(td) + except SkipTest as ex: + self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") + + assert not td.exists() + + @pytest.mark.skipif( sys.platform == "cygwin", - "Cygwin can't set the permissions that make the test meaningful.", + reason="Cygwin can't set the permissions that make the test meaningful.", ) - def test_rmtree_deletes_dir_with_readonly_files(self): - # Automatically works on Unix, but requires special handling on Windows. - # Not to be confused with what _tmpdir_to_force_permission_error sets up (see below). - with tempfile.TemporaryDirectory() as parent: - td = pathlib.Path(parent, "testdir") - for d in td, td / "sub": - d.mkdir() - for f in td / "x", td / "sub" / "y": - f.write_bytes(b"") - f.chmod(0) + def test_wraps_perm_error_if_enabled(self, mocker, permission_error_tmpdir): + """rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true.""" + # Access the module through sys.modules so it is unambiguous which module's + # attribute we patch: the original git.util, not git.index.util even though + # git.index.util "replaces" git.util and is what "import git.util" gives us. + mocker.patch.object(sys.modules["git.util"], "HIDE_WINDOWS_KNOWN_ERRORS", True) + # Disable common chmod functions so the callback can never fix the problem. + mocker.patch.object(os, "chmod") + mocker.patch.object(pathlib.Path, "chmod") + + # Now we can see how an intractable PermissionError is treated. + with pytest.raises(SkipTest): + rmtree(permission_error_tmpdir) + + @pytest.mark.skipif( + sys.platform == "cygwin", + reason="Cygwin can't set the permissions that make the test meaningful.", + ) + def test_does_not_wrap_perm_error_unless_enabled(self, mocker, permission_error_tmpdir): + """rmtree does not wrap PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is false.""" + # See comments in test_wraps_perm_error_if_enabled for details about patching. + mocker.patch.object(sys.modules["git.util"], "HIDE_WINDOWS_KNOWN_ERRORS", False) + mocker.patch.object(os, "chmod") + mocker.patch.object(pathlib.Path, "chmod") + + with pytest.raises(PermissionError): try: - rmtree(td) + rmtree(permission_error_tmpdir) + except SkipTest as ex: + pytest.fail(f"rmtree unexpectedly attempts skip: {ex!r}") + + @pytest.mark.parametrize("hide_windows_known_errors", [False, True]) + def test_does_not_wrap_other_errors(self, tmp_path, mocker, hide_windows_known_errors): + file_not_found_tmpdir = tmp_path / "testdir" # It is deliberately never created. + + # See comments in test_wraps_perm_error_if_enabled for details about patching. + mocker.patch.object(sys.modules["git.util"], "HIDE_WINDOWS_KNOWN_ERRORS", hide_windows_known_errors) + mocker.patch.object(os, "chmod") + mocker.patch.object(pathlib.Path, "chmod") + + with pytest.raises(FileNotFoundError): + try: + rmtree(file_not_found_tmpdir) except SkipTest as ex: self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") - self.assertFalse(td.exists()) - def test_rmtree_can_wrap_exceptions(self): - """rmtree wraps PermissionError when HIDE_WINDOWS_KNOWN_ERRORS is true.""" - with _tmpdir_to_force_permission_error() as td: - # Access the module through sys.modules so it is unambiguous which module's - # attribute we patch: the original git.util, not git.index.util even though - # git.index.util "replaces" git.util and is what "import git.util" gives us. - with mock.patch.object(sys.modules["git.util"], "HIDE_WINDOWS_KNOWN_ERRORS", True): - # Disable common chmod functions so the callback can't fix the problem. - with mock.patch.object(os, "chmod"), mock.patch.object(pathlib.Path, "chmod"): - # Now we can see how an intractable PermissionError is treated. - with self.assertRaises(SkipTest): - rmtree(td) +class TestEnvParsing: + """Tests for environment variable parsing logic in :mod:`git.util`.""" + + @staticmethod + def _run_parse(name, value): + command = [ + sys.executable, + "-c", + f"from git.util import {name}; print(repr({name}))", + ] + output = subprocess.check_output( + command, + env=None if value is None else dict(os.environ, **{name: value}), + text=True, + ) + return ast.literal_eval(output) - @ddt.data( - (False, PermissionError, _tmpdir_to_force_permission_error), - (False, FileNotFoundError, _tmpdir_for_file_not_found), - (True, FileNotFoundError, _tmpdir_for_file_not_found), - ) - def test_rmtree_does_not_wrap_unless_called_for(self, case): - """rmtree doesn't wrap non-PermissionError, nor if HIDE_WINDOWS_KNOWN_ERRORS is false.""" - hide_windows_known_errors, exception_type, tmpdir_context_factory = case - - with tmpdir_context_factory() as td: - # See comments in test_rmtree_can_wrap_exceptions regarding the patching done here. - with mock.patch.object( - sys.modules["git.util"], - "HIDE_WINDOWS_KNOWN_ERRORS", - hide_windows_known_errors, - ): - with mock.patch.object(os, "chmod"), mock.patch.object(pathlib.Path, "chmod"): - with self.assertRaises(exception_type): - try: - rmtree(td) - except SkipTest as ex: - self.fail(f"rmtree unexpectedly attempts skip: {ex!r}") - - @ddt.data("HIDE_WINDOWS_KNOWN_ERRORS", "HIDE_WINDOWS_FREEZE_ERRORS") - def test_env_vars_for_windows_tests(self, name): - def run_parse(value): - command = [ - sys.executable, - "-c", - f"from git.util import {name}; print(repr({name}))", - ] - output = subprocess.check_output( - command, - env=None if value is None else dict(os.environ, **{name: value}), - text=True, - ) - return ast.literal_eval(output) - - for env_var_value, expected_truth_value in ( + @pytest.mark.parametrize( + "env_var_value, expected_truth_value", + [ (None, os.name == "nt"), # True on Windows when the environment variable is unset. ("", False), (" ", False), @@ -193,79 +194,132 @@ def run_parse(value): ("YES", os.name == "nt"), (" no ", False), (" yes ", os.name == "nt"), - ): - with self.subTest(env_var_value=env_var_value): - self.assertIs(run_parse(env_var_value), expected_truth_value) - - _norm_cygpath_pairs = ( - (R"foo\bar", "foo/bar"), - (R"foo/bar", "foo/bar"), - (R"C:\Users", "/cygdrive/c/Users"), - (R"C:\d/e", "/cygdrive/c/d/e"), - ("C:\\", "/cygdrive/c/"), - (R"\\server\C$\Users", "//server/C$/Users"), - (R"\\server\C$", "//server/C$"), - ("\\\\server\\c$\\", "//server/c$/"), - (R"\\server\BAR/", "//server/BAR/"), - (R"D:/Apps", "/cygdrive/d/Apps"), - (R"D:/Apps\fOO", "/cygdrive/d/Apps/fOO"), - (R"D:\Apps/123", "/cygdrive/d/Apps/123"), + ], ) - - _unc_cygpath_pairs = ( - (R"\\?\a:\com", "/cygdrive/a/com"), - (R"\\?\a:/com", "/cygdrive/a/com"), - (R"\\?\UNC\server\D$\Apps", "//server/D$/Apps"), + @pytest.mark.parametrize( + "name", + [ + "HIDE_WINDOWS_KNOWN_ERRORS", + "HIDE_WINDOWS_FREEZE_ERRORS", + ], ) + def test_env_vars_for_windows_tests(self, name, env_var_value, expected_truth_value): + actual_parsed_value = self._run_parse(name, env_var_value) + assert actual_parsed_value is expected_truth_value + + +def _xfail_param(*values, **xfail_kwargs): + """Build a pytest.mark.parametrize parameter that carries an xfail mark.""" + return pytest.param(*values, marks=pytest.mark.xfail(**xfail_kwargs)) + + +_norm_cygpath_pairs = ( + (R"foo\bar", "foo/bar"), + (R"foo/bar", "foo/bar"), + (R"C:\Users", "/cygdrive/c/Users"), + (R"C:\d/e", "/cygdrive/c/d/e"), + ("C:\\", "/cygdrive/c/"), + (R"\\server\C$\Users", "//server/C$/Users"), + (R"\\server\C$", "//server/C$"), + ("\\\\server\\c$\\", "//server/c$/"), + (R"\\server\BAR/", "//server/BAR/"), + (R"D:/Apps", "/cygdrive/d/Apps"), + (R"D:/Apps\fOO", "/cygdrive/d/Apps/fOO"), + (R"D:\Apps/123", "/cygdrive/d/Apps/123"), +) - # FIXME: Mark only the /proc-prefixing cases xfail, somehow (or fix them). - @pytest.mark.xfail( - reason="Many return paths prefixed /proc/cygdrive instead.", - raises=AssertionError, - ) - @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.") - @ddt.idata(_norm_cygpath_pairs + _unc_cygpath_pairs) - def test_cygpath_ok(self, case): - wpath, cpath = case - cwpath = cygpath(wpath) - self.assertEqual(cwpath, cpath, wpath) +_unc_cygpath_pairs = ( + (R"\\?\a:\com", "/cygdrive/a/com"), + (R"\\?\a:/com", "/cygdrive/a/com"), + (R"\\?\UNC\server\D$\Apps", "//server/D$/Apps"), +) - @pytest.mark.xfail( - reason=R'2nd example r".\bar" -> "bar" fails, returns "./bar"', - raises=AssertionError, +# Mapping of expected failures for the test_cygpath_ok test. +_cygpath_ok_xfails = { + # From _norm_cygpath_pairs: + (R"C:\Users", "/cygdrive/c/Users"): "/proc/cygdrive/c/Users", + (R"C:\d/e", "/cygdrive/c/d/e"): "/proc/cygdrive/c/d/e", + ("C:\\", "/cygdrive/c/"): "/proc/cygdrive/c/", + (R"\\server\BAR/", "//server/BAR/"): "//server/BAR", + (R"D:/Apps", "/cygdrive/d/Apps"): "/proc/cygdrive/d/Apps", + (R"D:/Apps\fOO", "/cygdrive/d/Apps/fOO"): "/proc/cygdrive/d/Apps/fOO", + (R"D:\Apps/123", "/cygdrive/d/Apps/123"): "/proc/cygdrive/d/Apps/123", + # From _unc_cygpath_pairs: + (R"\\?\a:\com", "/cygdrive/a/com"): "/proc/cygdrive/a/com", + (R"\\?\a:/com", "/cygdrive/a/com"): "/proc/cygdrive/a/com", +} + + +# Parameter sets for the test_cygpath_ok test. +_cygpath_ok_params = [ + ( + _xfail_param(*case, reason=f"Returns: {_cygpath_ok_xfails[case]!r}", raises=AssertionError) + if case in _cygpath_ok_xfails + else case ) - @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.") - @ddt.data( - (R"./bar", "bar"), - (R".\bar", "bar"), # FIXME: Mark only this one xfail, somehow (or fix it). - (R"../bar", "../bar"), - (R"..\bar", "../bar"), - (R"../bar/.\foo/../chu", "../bar/chu"), + for case in _norm_cygpath_pairs + _unc_cygpath_pairs +] + + +@pytest.mark.skipif(sys.platform != "cygwin", reason="Paths specifically for Cygwin.") +class TestCygpath: + """Tests for :func:`git.util.cygpath` and :func:`git.util.decygpath`.""" + + @pytest.mark.parametrize("wpath, cpath", _cygpath_ok_params) + def test_cygpath_ok(self, wpath, cpath): + cwpath = cygpath(wpath) + assert cwpath == cpath, wpath + + @pytest.mark.parametrize( + "wpath, cpath", + [ + (R"./bar", "bar"), + _xfail_param(R".\bar", "bar", reason="Returns: './bar'", raises=AssertionError), + (R"../bar", "../bar"), + (R"..\bar", "../bar"), + (R"../bar/.\foo/../chu", "../bar/chu"), + ], ) - def test_cygpath_norm_ok(self, case): - wpath, cpath = case + def test_cygpath_norm_ok(self, wpath, cpath): cwpath = cygpath(wpath) - self.assertEqual(cwpath, cpath or wpath, wpath) - - @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.") - @ddt.data( - R"C:", - R"C:Relative", - R"D:Apps\123", - R"D:Apps/123", - R"\\?\a:rel", - R"\\share\a:rel", + assert cwpath == (cpath or wpath), wpath + + @pytest.mark.parametrize( + "wpath", + [ + R"C:", + R"C:Relative", + R"D:Apps\123", + R"D:Apps/123", + R"\\?\a:rel", + R"\\share\a:rel", + ], ) def test_cygpath_invalids(self, wpath): cwpath = cygpath(wpath) - self.assertEqual(cwpath, wpath.replace("\\", "/"), wpath) + assert cwpath == wpath.replace("\\", "/"), wpath - @skipUnless(sys.platform == "cygwin", "Paths specifically for Cygwin.") - @ddt.idata(_norm_cygpath_pairs) - def test_decygpath(self, case): - wpath, cpath = case + @pytest.mark.parametrize("wpath, cpath", _norm_cygpath_pairs) + def test_decygpath(self, wpath, cpath): wcpath = decygpath(cpath) - self.assertEqual(wcpath, wpath.replace("/", "\\"), cpath) + assert wcpath == wpath.replace("/", "\\"), cpath + + +class _Member: + """A member of an IterableList.""" + + __slots__ = ("name",) + + def __init__(self, name): + self.name = name + + def __repr__(self): + return f"{type(self).__name__}({self.name!r})" + + +@ddt.ddt +class TestUtils(TestBase): + """Tests for most utilities in :mod:`git.util`.""" def test_it_should_dashify(self): self.assertEqual("this-is-my-argument", dashify("this_is_my_argument")) @@ -275,14 +329,14 @@ def test_lock_file(self): my_file = tempfile.mktemp() lock_file = LockFile(my_file) assert not lock_file._has_lock() - # release lock we don't have - fine + # Release lock we don't have - fine. lock_file._release_lock() - # get lock + # Get lock. lock_file._obtain_lock_or_raise() assert lock_file._has_lock() - # concurrent access + # Concurrent access. other_lock_file = LockFile(my_file) assert not other_lock_file._has_lock() self.assertRaises(IOError, other_lock_file._obtain_lock_or_raise) @@ -293,7 +347,7 @@ def test_lock_file(self): other_lock_file._obtain_lock_or_raise() self.assertRaises(IOError, lock_file._obtain_lock_or_raise) - # auto-release on destruction + # Auto-release on destruction. del other_lock_file lock_file._obtain_lock_or_raise() lock_file._release_lock() @@ -303,7 +357,7 @@ def test_blocking_lock_file(self): lock_file = BlockingLockFile(my_file) lock_file._obtain_lock() - # next one waits for the lock + # Next one waits for the lock. start = time.time() wait_time = 0.1 wait_lock = BlockingLockFile(my_file, 0.05, wait_time) @@ -318,7 +372,7 @@ def test_user_id(self): self.assertIn("@", get_user_id()) def test_parse_date(self): - # parse_date(from_timestamp()) must return the tuple unchanged + # parse_date(from_timestamp()) must return the tuple unchanged. for timestamp, offset in ( (1522827734, -7200), (1522827734, 0), @@ -326,7 +380,7 @@ def test_parse_date(self): ): self.assertEqual(parse_date(from_timestamp(timestamp, offset)), (timestamp, offset)) - # test all supported formats + # Test all supported formats. def assert_rval(rval, veri_time, offset=0): self.assertEqual(len(rval), 2) self.assertIsInstance(rval[0], int) @@ -334,7 +388,7 @@ def assert_rval(rval, veri_time, offset=0): self.assertEqual(rval[0], veri_time) self.assertEqual(rval[1], offset) - # now that we are here, test our conversion functions as well + # Now that we are here, test our conversion functions as well. utctz = altz_to_utctz_str(offset) self.assertIsInstance(utctz, str) self.assertEqual(utctz_to_altz(verify_utctz(utctz)), offset) @@ -347,13 +401,13 @@ def assert_rval(rval, veri_time, offset=0): iso3 = ("2005.04.07 22:13:11 -0000", 0) alt = ("04/07/2005 22:13:11", 0) alt2 = ("07.04.2005 22:13:11", 0) - veri_time_utc = 1112911991 # the time this represents, in time since epoch, UTC + veri_time_utc = 1112911991 # The time this represents, in time since epoch, UTC. for date, offset in (rfc, iso, iso2, iso3, alt, alt2): assert_rval(parse_date(date), veri_time_utc, offset) # END for each date type - # and failure - self.assertRaises(ValueError, parse_date, datetime.now()) # non-aware datetime + # ...and failure. + self.assertRaises(ValueError, parse_date, datetime.now()) # Non-aware datetime. self.assertRaises(ValueError, parse_date, "invalid format") self.assertRaises(ValueError, parse_date, "123456789 -02000") self.assertRaises(ValueError, parse_date, " 123456789 -0200") @@ -362,7 +416,7 @@ def test_actor(self): for cr in (None, self.rorepo.config_reader()): self.assertIsInstance(Actor.committer(cr), Actor) self.assertIsInstance(Actor.author(cr), Actor) - # END assure config reader is handled + # END ensure config reader is handled @with_rw_repo("HEAD") @mock.patch("getpass.getuser") @@ -402,7 +456,7 @@ def test_actor_get_uid_laziness_called(self, mock_get_uid): mock_get_uid.return_value = "user" committer = Actor.committer(None) author = Actor.author(None) - # We can't test with `self.rorepo.config_reader()` here, as the uuid laziness + # We can't test with `self.rorepo.config_reader()` here, as the UUID laziness # depends on whether the user running the test has their global user.name config set. self.assertEqual(committer.name, "user") self.assertTrue(committer.email.startswith("user@")) @@ -436,30 +490,30 @@ def test_iterable_list(self, case): self.assertEqual(len(ilist), 2) - # contains works with name and identity + # Contains works with name and identity. self.assertIn(name1, ilist) self.assertIn(name2, ilist) self.assertIn(m2, ilist) self.assertIn(m2, ilist) self.assertNotIn("invalid", ilist) - # with string index + # With string index. self.assertIs(ilist[name1], m1) self.assertIs(ilist[name2], m2) - # with int index + # With int index. self.assertIs(ilist[0], m1) self.assertIs(ilist[1], m2) - # with getattr + # With getattr. self.assertIs(ilist.one, m1) self.assertIs(ilist.two, m2) - # test exceptions + # Test exceptions. self.assertRaises(AttributeError, getattr, ilist, "something") self.assertRaises(IndexError, ilist.__getitem__, "something") - # delete by name and index + # Delete by name and index. self.assertRaises(IndexError, ilist.__delitem__, "something") del ilist[name2] self.assertEqual(len(ilist), 1) @@ -494,21 +548,21 @@ def test_altz_to_utctz_str(self): self.assertEqual(altz_to_utctz_str(-59), "+0000") def test_from_timestamp(self): - # Correct offset: UTC+2, should return datetime + tzoffset(+2) + # Correct offset: UTC+2, should return datetime + tzoffset(+2). altz = utctz_to_altz("+0200") self.assertEqual( datetime.fromtimestamp(1522827734, tzoffset(altz)), from_timestamp(1522827734, altz), ) - # Wrong offset: UTC+58, should return datetime + tzoffset(UTC) + # Wrong offset: UTC+58, should return datetime + tzoffset(UTC). altz = utctz_to_altz("+5800") self.assertEqual( datetime.fromtimestamp(1522827734, tzoffset(0)), from_timestamp(1522827734, altz), ) - # Wrong offset: UTC-9000, should return datetime + tzoffset(UTC) + # Wrong offset: UTC-9000, should return datetime + tzoffset(UTC). altz = utctz_to_altz("-9000") self.assertEqual( datetime.fromtimestamp(1522827734, tzoffset(0)), @@ -538,7 +592,7 @@ def test_remove_password_from_command_line(self): redacted_cmd_1 = remove_password_if_present(cmd_1) assert username not in " ".join(redacted_cmd_1) assert password not in " ".join(redacted_cmd_1) - # Check that we use a copy + # Check that we use a copy. assert cmd_1 is not redacted_cmd_1 assert username in " ".join(cmd_1) assert password in " ".join(cmd_1)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: