From 8a60dd64419175835cda95b6f826786fbe47de55 Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 15:51:32 +0100 Subject: [PATCH 1/9] subprocess.check_output returns a str in Python 3 --- powerline_shell/segments/uptime.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/powerline_shell/segments/uptime.py b/powerline_shell/segments/uptime.py index 1c030611..4108ee60 100644 --- a/powerline_shell/segments/uptime.py +++ b/powerline_shell/segments/uptime.py @@ -1,19 +1,19 @@ import subprocess import re -from ..utils import BasicSegment, decode +from ..utils import BasicSegment class Segment(BasicSegment): def add_to_powerline(self): powerline = self.powerline try: - output = decode(subprocess.check_output(['uptime'], stderr=subprocess.STDOUT)) - raw_uptime = re.search('(?<=up).+(?=,\s+\d+\s+user)', output).group(0) - day_search = re.search('\d+(?=\s+day)', output) + output = subprocess.check_output(['uptime'], stderr=subprocess.STDOUT) + raw_uptime = re.search(r'(?<=up).+(?=,\s+\d+\s+user)', output).group(0) + day_search = re.search(r'\d+(?=\s+day)', output) days = '' if not day_search else '%sd ' % day_search.group(0) - hour_search = re.search('\d{1,2}(?=\:)', raw_uptime) + hour_search = re.search(r'\d{1,2}(?=\:)', raw_uptime) hours = '' if not hour_search else '%sh ' % hour_search.group(0) - minutes = re.search('(?<=\:)\d{1,2}|\d{1,2}(?=\s+min)', raw_uptime).group(0) + minutes = re.search(r'(?<=\:)\d{1,2}|\d{1,2}(?=\s+min)', raw_uptime).group(0) uptime = u' %s%s%sm \u2191 ' % (days, hours, minutes) powerline.append(uptime, powerline.theme.CWD_FG, powerline.theme.PATH_BG) except OSError: From 42edc0c298a4b127881b401137046295f53ca4fb Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 15:53:14 +0100 Subject: [PATCH 2/9] Use a contextmanager for changing directory sh.cd does not work anymore, as it does not persist between lines. This is cleaner, as we now always know what directory we're in. --- test/cwd_test.py | 19 ++++++++------- test/segments_test/bzr_test.py | 35 +++++++++++++++++----------- test/segments_test/fossil_test.py | 14 +++++++---- test/segments_test/git_stash_test.py | 10 ++++++-- test/segments_test/git_test.py | 13 ++++++++--- test/segments_test/hg_test.py | 12 +++++++--- test/segments_test/svn_test.py | 4 ++-- 7 files changed, 72 insertions(+), 35 deletions(-) diff --git a/test/cwd_test.py b/test/cwd_test.py index ed52dc36..9d9bff7f 100644 --- a/test/cwd_test.py +++ b/test/cwd_test.py @@ -3,6 +3,9 @@ import os import tempfile import shutil + +import sh + import powerline_shell as p @@ -33,9 +36,9 @@ def test_nonexistent_warns(self, warn, getenv): @mock.patch('powerline_shell.warn') def test_falls_back_to_getcwd(self, warn, getenv): getenv.return_value = None - os.chdir(self.dirname) - self.assertEqual(p.get_valid_cwd(), self.dirname) - self.assertEqual(warn.call_count, 0) + with sh.pushd(self.dirname): + self.assertEqual(p.get_valid_cwd(), self.dirname) + self.assertEqual(warn.call_count, 0) @mock.patch('os.getenv') @mock.patch('powerline_shell.warn') @@ -44,10 +47,10 @@ def test_nonexistent_getcwd_warns(self, warn, getenv): getenv.return_value = None os.mkdir(subdir) - os.chdir(subdir) - os.rmdir(subdir) + with sh.pushd(subdir): + os.rmdir(subdir) - with self.assertRaises(SystemExit) as e: - p.get_valid_cwd() + with self.assertRaises(SystemExit) as e: + p.get_valid_cwd() - self.assertEqual(warn.call_count, 1) + self.assertEqual(warn.call_count, 1) diff --git a/test/segments_test/bzr_test.py b/test/segments_test/bzr_test.py index 73dbebae..ad09a027 100644 --- a/test/segments_test/bzr_test.py +++ b/test/segments_test/bzr_test.py @@ -1,4 +1,6 @@ import unittest +from contextlib import ExitStack + import mock import tempfile import shutil @@ -28,14 +30,20 @@ def setUp(self): }) self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - sh.bzr("init-repo", ".") - sh.mkdir("trunk") - sh.cd("trunk") - sh.bzr("init") + with sh.pushd(self.dirname): + sh.bzr("init-repo", ".") + sh.mkdir("trunk") + + with sh.pushd(self.dirname + "/trunk"): + sh.bzr("init") + sh.bzr("whoami", "--branch", '"Example "') self.segment = bzr.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.dirname + "/trunk")) + self.addCleanup(stack.pop_all().close) + def tearDown(self): shutil.rmtree(self.dirname) @@ -45,9 +53,8 @@ def _add_and_commit(self, filename): sh.bzr("commit", "-m", "add file " + filename) def _checkout_new_branch(self, branch): - sh.cd("..") - sh.bzr("branch", "trunk", branch) - sh.cd(branch) + with sh.pushd(self.dirname): + sh.bzr("branch", "trunk", branch) @mock.patch("powerline_shell.utils.get_PATH") def test_bzr_not_installed(self, get_PATH): @@ -71,12 +78,14 @@ def test_trunk(self): def test_different_branch(self): self._add_and_commit("foo") self._checkout_new_branch("bar") - self.segment.start() - self.segment.add_to_powerline() - self.assertEqual(self.powerline.append.call_args[0][0], " bar ") + with sh.pushd(self.dirname + "/bar"): + self.segment.start() + self.segment.add_to_powerline() + self.assertEqual(self.powerline.append.call_args[0][0], " bar ") @mock.patch('powerline_shell.segments.bzr._get_bzr_status') def test_all(self, check_output): for stdout, result in test_cases: - stats = bzr.parse_bzr_stats(stdout) - self.assertEquals(result, stats) + with sh.pushd(self.dirname + "/trunk"): + stats = bzr.parse_bzr_stats(stdout) + self.assertEqual(result, stats) diff --git a/test/segments_test/fossil_test.py b/test/segments_test/fossil_test.py index 34f914af..b7fe0252 100644 --- a/test/segments_test/fossil_test.py +++ b/test/segments_test/fossil_test.py @@ -1,4 +1,6 @@ import unittest +from contextlib import ExitStack + import mock import tempfile import shutil @@ -24,12 +26,16 @@ def setUp(self): }) self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - sh.fossil("init", "test.fossil") - sh.fossil("open", "test.fossil") + with sh.pushd(self.dirname): + sh.fossil("init", "test.fossil") + sh.fossil("open", "test.fossil") self.segment = fossil.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.dirname)) + self.addCleanup(stack.pop_all().close) + def tearDown(self): shutil.rmtree(self.dirname) @@ -72,4 +78,4 @@ def test_different_branch(self): def test_all(self, check_output): for stdout, result in test_cases.items(): stats = fossil.parse_fossil_stats([stdout]) - self.assertEquals(result, stats) + self.assertEqual(result, stats) diff --git a/test/segments_test/git_stash_test.py b/test/segments_test/git_stash_test.py index 1b228cd6..2be0f21c 100644 --- a/test/segments_test/git_stash_test.py +++ b/test/segments_test/git_stash_test.py @@ -1,4 +1,6 @@ import unittest +from contextlib import ExitStack + import mock import tempfile import shutil @@ -12,11 +14,15 @@ class GitStashTest(unittest.TestCase): def setUp(self): self.powerline = mock.MagicMock() self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - sh.git("init", ".") + with sh.pushd(self.dirname): + sh.git("init", ".") self.segment = git_stash.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.dirname)) + self.addCleanup(stack.pop_all().close) + def tearDown(self): shutil.rmtree(self.dirname) diff --git a/test/segments_test/git_test.py b/test/segments_test/git_test.py index 373fd68f..57470589 100644 --- a/test/segments_test/git_test.py +++ b/test/segments_test/git_test.py @@ -1,4 +1,6 @@ import unittest +from contextlib import ExitStack + import mock import tempfile import shutil @@ -16,11 +18,16 @@ def setUp(self): }) self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - sh.git("init", ".") + with sh.pushd(self.dirname): + sh.git("init", ".") self.segment = git.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.dirname)) + self.addCleanup(stack.pop_all().close) + + def tearDown(self): shutil.rmtree(self.dirname) @@ -57,7 +64,7 @@ def test_master_branch(self): self._add_and_commit("foo") self.segment.start() self.segment.add_to_powerline() - self.assertEqual(self.powerline.append.call_args[0][0], ' master ') + self.assertIn(self.powerline.append.call_args[0][0], [' master ', ' main ']) def test_different_branch(self): self._add_and_commit("foo") diff --git a/test/segments_test/hg_test.py b/test/segments_test/hg_test.py index 004eac9c..d6fb1c84 100644 --- a/test/segments_test/hg_test.py +++ b/test/segments_test/hg_test.py @@ -1,4 +1,6 @@ import unittest +from contextlib import ExitStack + import mock import tempfile import shutil @@ -26,11 +28,15 @@ def setUp(self): }) self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - sh.hg("init", ".") + with sh.pushd(self.dirname): + sh.hg("init", ".") self.segment = hg.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.dirname)) + self.addCleanup(stack.pop_all().close) + def tearDown(self): shutil.rmtree(self.dirname) @@ -72,4 +78,4 @@ def test_different_branch(self): def test_all(self, check_output): for stdout, result in test_cases.items(): stats = hg.parse_hg_stats([stdout]) - self.assertEquals(result, stats) + self.assertEqual(result, stats) diff --git a/test/segments_test/svn_test.py b/test/segments_test/svn_test.py index d7d821f8..1e213133 100644 --- a/test/segments_test/svn_test.py +++ b/test/segments_test/svn_test.py @@ -16,8 +16,8 @@ def setUp(self): }) self.dirname = tempfile.mkdtemp() - sh.cd(self.dirname) - # sh.svn("init", ".") + with sh.pushd(self.dirname): + sh.svn("init", ".") self.segment = svn.Segment(self.powerline, {}) From 71a88a8c8cb8348f49ac9652c0dd099869a11c35 Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 15:53:41 +0100 Subject: [PATCH 3/9] Python 3 uses range instead of xrange --- test/color_compliment_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/color_compliment_test.py b/test/color_compliment_test.py index 47f72d07..e022c1ed 100644 --- a/test/color_compliment_test.py +++ b/test/color_compliment_test.py @@ -7,7 +7,7 @@ def build_inputs(): # Build 768 hex/rgb values to test against getOppositeColor - input_bytes = map(hex, xrange(pow(2,8))) + input_bytes = list(map(hex, range(pow(2,8)))) input_list = [] for x in input_bytes: From b33995c9d81944e0e47146f8409fb452dc65b309 Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 15:59:58 +0100 Subject: [PATCH 4/9] Run tests in GitHub Actions container --- .github/workflows/test.yaml | 37 +++++++++++++++++++++++++++++++++++++ requirements-dev.txt | 3 ++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..e4b05bc1 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,37 @@ +name: Python Tests + +on: + - push + +jobs: + test: + runs-on: ubuntu-latest + + container: + image: ubuntu:22.04 + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install binary dependencies + run: apt update && apt install -y git bzr fossil mercurial subversion + env: + DEBIAN_FRONTEND: noninteractive + + - name: Install Python dependencies + run: python -m pip install -r requirements-dev.txt + + - name: Run Python Tests + run: python -m pytest --junitxml=test-results.xml + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4 + if: success() || failure() # always run even if the previous step fails + with: + report_paths: 'test-results.xml' diff --git a/requirements-dev.txt b/requirements-dev.txt index 59d5eef7..5f3eaf17 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,5 @@ nose>=1.3.7 mock>=1.3.0 sh>=1.11 -parameterized>=0.6.1 \ No newline at end of file +parameterized>=0.6.1 +pytest From ed09a587095ee55a7a09254451a1dd746a945bed Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 16:21:17 +0100 Subject: [PATCH 5/9] Set username and email for Git --- test/segments_test/git_stash_test.py | 2 ++ test/segments_test/git_test.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/segments_test/git_stash_test.py b/test/segments_test/git_stash_test.py index 2be0f21c..69de9946 100644 --- a/test/segments_test/git_stash_test.py +++ b/test/segments_test/git_stash_test.py @@ -16,6 +16,8 @@ def setUp(self): self.dirname = tempfile.mkdtemp() with sh.pushd(self.dirname): sh.git("init", ".") + sh.git("config", "user.name", "Test") + sh.git("config", "user.email", "example@example.com") self.segment = git_stash.Segment(self.powerline, {}) diff --git a/test/segments_test/git_test.py b/test/segments_test/git_test.py index 57470589..a6fc6c2c 100644 --- a/test/segments_test/git_test.py +++ b/test/segments_test/git_test.py @@ -20,6 +20,8 @@ def setUp(self): self.dirname = tempfile.mkdtemp() with sh.pushd(self.dirname): sh.git("init", ".") + sh.git("config", "user.name", "Test") + sh.git("config", "user.email", "example@example.com") self.segment = git.Segment(self.powerline, {}) From bd999398d1f31628f2728dcc35bae14f43e68ca8 Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 16:31:39 +0100 Subject: [PATCH 6/9] Initialise and checkout an svn repo --- test/segments_test/svn_test.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/test/segments_test/svn_test.py b/test/segments_test/svn_test.py index 1e213133..e3460835 100644 --- a/test/segments_test/svn_test.py +++ b/test/segments_test/svn_test.py @@ -1,6 +1,8 @@ import tempfile import unittest import shutil +from contextlib import ExitStack + import mock import sh import powerline_shell.segments.svn as svn @@ -15,18 +17,24 @@ def setUp(self): ("vcs", "show_symbol"): False, }) - self.dirname = tempfile.mkdtemp() - with sh.pushd(self.dirname): - sh.svn("init", ".") + self.upstream_dir = tempfile.mkdtemp() + self.checkout_dir = tempfile.mkdtemp() + sh.svnadmin("create", self.upstream_dir) + sh.svn("checkout", f"file://{self.upstream_dir}", self.checkout_dir) self.segment = svn.Segment(self.powerline, {}) + with ExitStack() as stack: + self._resource = stack.enter_context(sh.pushd(self.checkout_dir)) + self.addCleanup(stack.pop_all().close) + def tearDown(self): - shutil.rmtree(self.dirname) + shutil.rmtree(self.upstream_dir) + shutil.rmtree(self.checkout_dir) @mock.patch("powerline_shell.utils.get_PATH") def test_svn_not_installed(self, get_PATH): - get_PATH.return_value = "" # so svn can't be found + get_PATH.return_value = "" # so svn can't be found self.segment.start() self.segment.add_to_powerline() self.assertEqual(self.powerline.append.call_count, 0) From 45f063bd8e9d70cd9c4323d1d3521a81b412a13d Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 16:39:20 +0100 Subject: [PATCH 7/9] Set the environment variable fossil needs --- test/segments_test/fossil_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/segments_test/fossil_test.py b/test/segments_test/fossil_test.py index b7fe0252..52f09c43 100644 --- a/test/segments_test/fossil_test.py +++ b/test/segments_test/fossil_test.py @@ -1,5 +1,6 @@ import unittest from contextlib import ExitStack +from os import environ import mock import tempfile @@ -19,6 +20,7 @@ class FossilTest(unittest.TestCase): + @mock.patch.dict(environ, {"USER": "root"}) def setUp(self): self.powerline = mock.MagicMock() self.powerline.segment_conf.side_effect = dict_side_effect_fn({ @@ -61,12 +63,14 @@ def test_non_fossil_directory(self): self.segment.add_to_powerline() self.assertEqual(self.powerline.append.call_count, 0) + @mock.patch.dict(environ, {"USER": "root"}) def test_standard(self): self._add_and_commit("foo") self.segment.start() self.segment.add_to_powerline() self.assertEqual(self.powerline.append.call_args[0][0], " trunk ") + @mock.patch.dict(environ, {"USER": "root"}) def test_different_branch(self): self._add_and_commit("foo") self._checkout_new_branch("bar") From 4b55755c03bac7122087635a27bb542cab9e689e Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 20:05:56 +0100 Subject: [PATCH 8/9] Remove reference to Nose Nose v1 is deprecated, use Pytest instead. --- requirements-dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5f3eaf17..4ea5729b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,3 @@ -nose>=1.3.7 mock>=1.3.0 sh>=1.11 parameterized>=0.6.1 From 61e7e9a215b647e703ab7ad04b003fdce38280cc Mon Sep 17 00:00:00 2001 From: Daniel Milnes Date: Sun, 1 Oct 2023 20:08:12 +0100 Subject: [PATCH 9/9] Remove reference to Mock Since Python 3.3, unittest.mock has been part of the standard library --- requirements-dev.txt | 1 - test/cwd_test.py | 2 +- test/segments_test/bzr_test.py | 2 +- test/segments_test/fossil_test.py | 2 +- test/segments_test/git_stash_test.py | 2 +- test/segments_test/git_test.py | 2 +- test/segments_test/hg_test.py | 2 +- test/segments_test/hostname_test.py | 2 +- test/segments_test/svn_test.py | 2 +- test/segments_test/uptime_test.py | 2 +- 10 files changed, 9 insertions(+), 10 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 4ea5729b..b440b50a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,3 @@ -mock>=1.3.0 sh>=1.11 parameterized>=0.6.1 pytest diff --git a/test/cwd_test.py b/test/cwd_test.py index 9d9bff7f..00ae5faf 100644 --- a/test/cwd_test.py +++ b/test/cwd_test.py @@ -1,5 +1,5 @@ import unittest -import mock +from unittest import mock import os import tempfile import shutil diff --git a/test/segments_test/bzr_test.py b/test/segments_test/bzr_test.py index ad09a027..813a41a4 100644 --- a/test/segments_test/bzr_test.py +++ b/test/segments_test/bzr_test.py @@ -1,7 +1,7 @@ import unittest from contextlib import ExitStack -import mock +from unittest import mock import tempfile import shutil import sh diff --git a/test/segments_test/fossil_test.py b/test/segments_test/fossil_test.py index 52f09c43..dd45b923 100644 --- a/test/segments_test/fossil_test.py +++ b/test/segments_test/fossil_test.py @@ -2,7 +2,7 @@ from contextlib import ExitStack from os import environ -import mock +from unittest import mock import tempfile import shutil import sh diff --git a/test/segments_test/git_stash_test.py b/test/segments_test/git_stash_test.py index 69de9946..3285f906 100644 --- a/test/segments_test/git_stash_test.py +++ b/test/segments_test/git_stash_test.py @@ -1,7 +1,7 @@ import unittest from contextlib import ExitStack -import mock +from unittest import mock import tempfile import shutil import sh diff --git a/test/segments_test/git_test.py b/test/segments_test/git_test.py index a6fc6c2c..b4e94aea 100644 --- a/test/segments_test/git_test.py +++ b/test/segments_test/git_test.py @@ -1,7 +1,7 @@ import unittest from contextlib import ExitStack -import mock +from unittest import mock import tempfile import shutil import sh diff --git a/test/segments_test/hg_test.py b/test/segments_test/hg_test.py index d6fb1c84..27944e95 100644 --- a/test/segments_test/hg_test.py +++ b/test/segments_test/hg_test.py @@ -1,7 +1,7 @@ import unittest from contextlib import ExitStack -import mock +from unittest import mock import tempfile import shutil import sh diff --git a/test/segments_test/hostname_test.py b/test/segments_test/hostname_test.py index 05ae1f1b..43f2b59f 100644 --- a/test/segments_test/hostname_test.py +++ b/test/segments_test/hostname_test.py @@ -1,5 +1,5 @@ import unittest -import mock +from unittest import mock import powerline_shell.segments.hostname as hostname from powerline_shell.themes.default import Color from argparse import Namespace diff --git a/test/segments_test/svn_test.py b/test/segments_test/svn_test.py index e3460835..d615ad8f 100644 --- a/test/segments_test/svn_test.py +++ b/test/segments_test/svn_test.py @@ -3,7 +3,7 @@ import shutil from contextlib import ExitStack -import mock +from unittest import mock import sh import powerline_shell.segments.svn as svn from ..testing_utils import dict_side_effect_fn diff --git a/test/segments_test/uptime_test.py b/test/segments_test/uptime_test.py index 676b193e..b515e7e3 100644 --- a/test/segments_test/uptime_test.py +++ b/test/segments_test/uptime_test.py @@ -1,5 +1,5 @@ import unittest -import mock +from unittest import mock import powerline_shell.segments.uptime as uptime test_cases = {