Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mocking-based unit test for the status command #67

Merged
merged 34 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2ab3932
Add files via upload
SimonL22 Aug 14, 2024
ac200b0
Update test_status_execute_mocken.py
SimonL22 Aug 14, 2024
1704173
Update test_status_execute_mocken.py
SimonL22 Aug 14, 2024
953358f
Update test_status_execute_mocken.py
SimonL22 Aug 14, 2024
96ddcac
Update test_status_execute_mocken.py
SimonL22 Aug 14, 2024
12537dd
Update test_status_execute_mocken.py
SimonL22 Aug 20, 2024
6a8046d
Create pytest.yml
SimonL22 Aug 20, 2024
90fc463
Merge branch 'ad-freiburg:main' into test_status_execute_mocken
SimonL22 Aug 21, 2024
6704850
Create test_stop_execute
SimonL22 Aug 21, 2024
b58d7dd
Update and rename test/qlever/test_status_execute_mocken.py to test/q…
SimonL22 Aug 21, 2024
d46e928
Update test_status_execute_mocken.py
SimonL22 Aug 21, 2024
a62da4d
Add files via upload
SimonL22 Aug 21, 2024
98811a8
Update test_status_other_methods.py
SimonL22 Aug 26, 2024
6fe6558
Update pytest.yml
SimonL22 Aug 27, 2024
b18c36f
Merge branch 'ad-freiburg:main' into test_status_execute_mocken
SimonL22 Aug 31, 2024
e50ef8b
Update pytest.yml
SimonL22 Sep 10, 2024
549132c
Update pytest.yml
SimonL22 Sep 10, 2024
c22f8db
Update pytest.yml
SimonL22 Sep 10, 2024
50468f2
Update pytest.yml
SimonL22 Sep 10, 2024
bbc593a
Update and rename test_status_execute_mocken.py to test_status_execut…
SimonL22 Sep 10, 2024
47ed435
Update test_status_execute.py
SimonL22 Sep 12, 2024
7a656ea
Update test_status_other_methods.py
SimonL22 Sep 12, 2024
833bad6
Update test_status_execute.py
SimonL22 Sep 12, 2024
f6c759d
Update test_status_other_methods.py
SimonL22 Sep 12, 2024
d3c020b
Delete test/qlever/commands/test_stop_execute
SimonL22 Sep 12, 2024
c1fd7b7
Update test_status_other_methods.py
SimonL22 Oct 9, 2024
fb7bf53
Update test_status_execute.py
SimonL22 Oct 9, 2024
80d0c48
Update test_status_execute.py
SimonL22 Oct 9, 2024
00618dc
Merge branch 'ad-freiburg:main' into test_status_with_mocking
SimonL22 Nov 3, 2024
e446092
Merge branch 'ad-freiburg:main' into test_status_with_mocking
SimonL22 Dec 23, 2024
6ef67ed
formatting
joka921 Dec 23, 2024
1cf6d16
fix tests
joka921 Dec 23, 2024
2fc801a
formatting fixed
joka921 Dec 23, 2024
5fb5ab0
also sort the imports via ruff
joka921 Dec 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Unit Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
unit_tests:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["pypy3.9", "pypy3.10", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{matrix.python-version}}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install .
pip install pytest pytest-cov
- name: Test with pytest
run: |
pytest -v
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ package-data = { "qlever" = ["Qleverfiles/*"] }

[tool.pytest.ini_options]
pythonpath = ["src"]

[tool.ruff]
line-length = 79
[tool.ruff.lint]
extend-select = ["I"]
140 changes: 140 additions & 0 deletions test/qlever/commands/test_status_execute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import sys
import unittest
from io import StringIO
from unittest.mock import MagicMock, call, patch

import qlever.command
from qlever.commands.status import StatusCommand


def get_mock_args(only_show):
args = MagicMock()
args.cmdline_regex = "^(ServerMain|IndexBuilderMain)"
args.show = only_show
return [args, args.cmdline_regex, args.show]


class TestStatusCommand(unittest.TestCase):
@patch("qlever.commands.status.show_process_info")
@patch("psutil.process_iter")
# testing execute for 2 processes. Just the second one is a qlever process.
# Mocking the process_iter and show_process_info method and testing
# if the methods are called correctly.
def test_execute_processes_found(
self, mock_process_iter, mock_show_process_info
):
# Mocking the input for the execute function
[args, args.cmdline_regex, args.show] = get_mock_args(False)

# Creating mock psutil.Process objects with necessary attributes
mock_process1 = MagicMock()
mock_process1.as_dict.return_value = {"test": [1]}
# to test with real psutil.process objects use this:
"""mock_process1.as_dict.return_value = {
'cmdline': ['cmdline1'],
'pid': 1,
'username': 'user1',
'create_time': datetime.now().timestamp(),
'memory_info': MagicMock(rss=512 * 1024 * 1024) # 512 MB
}"""

mock_process2 = MagicMock()
mock_process2.as_dict.return_value = {"test": [2]}
# to test with real psutil.process objects use this:
"""mock_process2.as_dict.return_value = {
'cmdline': ['cmdline2'],
'pid': 2,
'username': 'user2',
'create_time': datetime.now().timestamp(),
'memory_info': MagicMock(rss=1024 * 1024 * 1024) # 1 GB
}"""

mock_process3 = MagicMock()
mock_process3.as_dict.return_value = {"test": [3]}

# Mock the return value of process_iter
# to be a list of these mocked process objects
mock_process_iter.return_value = [
mock_process1,
mock_process2,
mock_process3,
]

# Simulate show_process_info returning False for the first
# True for the second and False for the third process
mock_show_process_info.side_effect = [False, True, False]

sc = StatusCommand()

# Execute the function
result = sc.execute(args)

# Assert that process_iter was called once
mock_process_iter.assert_called_once()

# Assert that show_process_info was called 3times
# in correct order with the correct arguments
expected_calls = [
call(mock_process1, args.cmdline_regex, show_heading=True),
call(mock_process2, args.cmdline_regex, show_heading=True),
call(mock_process3, args.cmdline_regex, show_heading=False),
]
mock_show_process_info.assert_has_calls(
expected_calls, any_order=False
)
self.assertTrue(result)

@patch("qlever.util.show_process_info")
@patch("psutil.process_iter")
def test_execute_no_processes_found(
self, mock_process_iter, mock_show_process_info
):
# Mocking the input for the execute function
[args, args.cmdline_regex, args.show] = get_mock_args(False)

# Mock process_iter to return an empty list,
# simulating that no matching processes are found
mock_process_iter.return_value = []

# Capture the string-output
captured_output = StringIO()
sys.stdout = captured_output

# Instantiate the StatusCommand
status_command = StatusCommand()

# Execute the function
result = status_command.execute(args)

# Reset redirect
sys.stdout = sys.__stdout__

# Assert that process_iter was called once
mock_process_iter.assert_called_once()

# Assert that show_process_info was never called
# since there are no processes
mock_show_process_info.assert_not_called()

self.assertTrue(result)

# Verify the correct output was printed
self.assertIn("No processes found", captured_output.getvalue())

@patch.object(qlever.command.QleverCommand, "show")
def test_execute_show_action_description(self, mock_show):
# Mocking the input for the execute function
[args, args.cmdline_regex, args.show] = get_mock_args(True)

# Execute the function
result = StatusCommand().execute(args)

# Assert that verifies that show was called with the correct parameters
mock_show.assert_any_call(
f"Show all processes on this machine where "
f"the command line matches {args.cmdline_regex}"
f" using Python's psutil library",
only_show=args.show,
)

self.assertTrue(result)
41 changes: 41 additions & 0 deletions test/qlever/commands/test_status_other_methods.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import argparse
import unittest

from qlever.commands.status import StatusCommand


class TestStatusCommand(unittest.TestCase):
def test_description(self):
result = StatusCommand().description()
self.assertEqual(
result, "Show QLever processes running on this machine"
)

def test_should_have_qleverfile(self):
self.assertFalse(StatusCommand().should_have_qleverfile())

def test_relevant_qleverfile_arguments(self):
result = StatusCommand().relevant_qleverfile_arguments()
self.assertEqual(result, {})
joka921 marked this conversation as resolved.
Show resolved Hide resolved

def test_additional_arguments(self):
# Create an instance of StatusCommand
sc = StatusCommand()

# Create a parser and a subparser
parser = argparse.ArgumentParser()
subparser = parser.add_argument_group("test")
# Call the method
sc.additional_arguments(subparser)
# Parse an empty argument list to see the default
args = parser.parse_args([])

# Test that the default value is set correctly
self.assertEqual(args.cmdline_regex, "^(ServerMain|IndexBuilderMain)")

# Test that the help text is correctly set
argument_help = subparser._group_actions[-1].help
self.assertEqual(
argument_help,
"Show only processes where the command line matches this regex",
)
Loading