-
Notifications
You must be signed in to change notification settings - Fork 90
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
Implementation for ImageService.List #206
Changes from all commits
e0aebbd
1130840
56663a5
35f60a7
a0b223b
d8b5708
c8104f5
01d7eca
ab8fa1a
8dd89cd
56e6115
02b402c
1ceda79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
import os | ||
import stat | ||
import pytest | ||
import json | ||
from unittest import mock | ||
from host_modules.image_service import ImageService | ||
|
||
|
@@ -370,3 +371,200 @@ def test_checksum_general_exception( | |
), "message should contain 'general error'" | ||
mock_isfile.assert_called_once_with(file_path) | ||
mock_open.assert_called_once_with(file_path, "rb") | ||
|
||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_success(self, mock_check_output, MockInit, MockBusName, MockSystemBus): | ||
""" | ||
Test that the `list_images` method successfully lists the current, next, and available SONiC images. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_output = ( | ||
"Current: current_image\n" | ||
"Next: next_image\n" | ||
"Available:\n" | ||
"image1\n" | ||
"image2\n" | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There would be multiple combination for the output, you may have to test and retrieve them in multiple versions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean? All we care about is the service correctly output all of "current", "next" and "available" images, so adding different combinations doesn't really allow us to catch more bugs, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you check the implementation, you will notice that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the clarification. Added testcase for outputs like "Current: blah\nNext: blah\n". |
||
mock_check_output.return_value = mock_output.encode() | ||
|
||
# Act | ||
rc, images_json = image_service.list_images() | ||
images = json.loads(images_json) | ||
|
||
# Assert | ||
assert rc == 0, "wrong return value" | ||
assert images["current"] == "current_image", "current image does not match" | ||
assert images["next"] == "next_image", "next image does not match" | ||
assert images["available"] == ["image1", "image2"], "available images do not match" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_success_lowercase_output(self, mock_check_output, MockInit, MockBusName, MockSystemBus): | ||
""" | ||
Test that the `list_images` method successfully lists the current, next, and available SONiC images | ||
even if the output from sonic-installer is in lowercase. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_output = ( | ||
"current: current_image\n" | ||
"next: next_image\n" | ||
"available:\n" | ||
"image1\n" | ||
"image2\n" | ||
) | ||
mock_check_output.return_value = mock_output.encode() | ||
|
||
# Act | ||
rc, images_json = image_service.list_images() | ||
images = json.loads(images_json) | ||
|
||
# Assert | ||
assert rc == 0, "wrong return value" | ||
assert images["current"] == "current_image", "current image does not match" | ||
assert images["next"] == "next_image", "next image does not match" | ||
assert images["available"] == ["image1", "image2"], "available images do not match" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
||
@pytest.mark.parametrize( | ||
"mock_output, expected_current, expected_next, expected_available", | ||
[ | ||
("Current: \nNext: next_image\nAvailable:\nimage1\nimage2\n", "", "next_image", ["image1", "image2"]), | ||
("Current: current_image\nNext: \nAvailable:\nimage1\nimage2\n", "current_image", "", ["image1", "image2"]), | ||
("Current: current_image\nNext: next_image\nAvailable:\n", "current_image", "next_image", []), | ||
], | ||
) | ||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_success_empty_image_output( | ||
self, mock_check_output, MockInit, MockBusName, MockSystemBus, mock_output, expected_current, expected_next, expected_available | ||
): | ||
""" | ||
Test that the `list_images` method successfully lists the current, next, and available SONiC images even if | ||
sonic-installer output empty string for the image name. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_check_output.return_value = mock_output.encode() | ||
|
||
# Act | ||
rc, images_json = image_service.list_images() | ||
images = json.loads(images_json) | ||
|
||
# Assert | ||
assert rc == 0, "wrong return value" | ||
assert images["current"] == expected_current, "current image does not match" | ||
assert images["next"] == expected_next, "next image does not match" | ||
assert images["available"] == expected_available, "available images do not match" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
||
@pytest.mark.parametrize( | ||
"mock_output, expected_current, expected_next, expected_available", | ||
[ | ||
("Next: next_image\nAvailable:\nimage1\nimage2\n", "", "next_image", ["image1", "image2"]), | ||
("Current: current_image\nAvailable:\nimage1\nimage2\n", "current_image", "", ["image1", "image2"]), | ||
("Current: current_image\nNext: next_image\n", "current_image", "next_image", []), | ||
("Available:\nimage1\nimage2\n", "", "", ["image1", "image2"]), | ||
("Current: current_image\nNext: next_image\nAvailable:\n", "current_image", "next_image", []), | ||
], | ||
) | ||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_various_missing_lines( | ||
self, mock_check_output, MockInit, MockBusName, MockSystemBus, mock_output, expected_current, expected_next, expected_available | ||
): | ||
""" | ||
Test that the `list_images` method handles various scenarios where the sonic-installer output is missing lines for current, next, or available images. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_check_output.return_value = mock_output.encode() | ||
|
||
# Act | ||
rc, images_json = image_service.list_images() | ||
images = json.loads(images_json) | ||
|
||
# Assert | ||
assert rc == 0, "wrong return value" | ||
assert images["current"] == expected_current, "current image does not match" | ||
assert images["next"] == expected_next, "next image does not match" | ||
assert images["available"] == expected_available, "available images do not match" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_success_empty_available_images(self, mock_check_output, MockInit, MockBusName, MockSystemBus): | ||
""" | ||
Test that the `list_images` method successfully lists the current, next, and available SONiC images. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_output = ( | ||
"Current: current_image\n" | ||
"Next: next_image\n" | ||
"Available:\n" | ||
) | ||
mock_check_output.return_value = mock_output.encode() | ||
|
||
# Act | ||
rc, images_json = image_service.list_images() | ||
images = json.loads(images_json) | ||
|
||
# Assert | ||
assert rc == 0, "wrong return value" | ||
assert images["available"] == [], "available images should be empty" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
||
|
||
@mock.patch("dbus.SystemBus") | ||
@mock.patch("dbus.service.BusName") | ||
@mock.patch("dbus.service.Object.__init__") | ||
@mock.patch("subprocess.check_output") | ||
def test_list_images_failed(self, mock_check_output, MockInit, MockBusName, MockSystemBus): | ||
""" | ||
Test that the `list_image` method fails when the subprocess command returns a non-zero exit code. | ||
""" | ||
# Arrange | ||
image_service = ImageService(mod_name="image_service") | ||
mock_check_output.side_effect = subprocess.CalledProcessError( | ||
returncode=1, cmd="sonic-installer list", output=b"Error: command failed" | ||
) | ||
|
||
# Act | ||
rc, msg = image_service.list_images() | ||
|
||
# Assert | ||
assert rc != 0, "wrong return value" | ||
mock_check_output.assert_called_once_with( | ||
["/usr/local/bin/sonic-installer", "list"], | ||
stderr=subprocess.STDOUT, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add log for raw output for debugging purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still applicable, before we do parse, we could log it for future debug purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have logs for 1. before calling sonic-installer, 2. the output of sonic-installer before we try to parse it, as well as 3. when sonic-installer have a nonzero rc. Added one for parse result of sonic-installer output.