STAR-322: Using QNetworkReply.finished signal instead of QNetworkAccessManager.finished

This commit is contained in:
Daniel Schiavini 2018-12-14 14:50:15 +01:00
parent 4dc8edb996
commit 2f08854097
9 changed files with 152 additions and 164 deletions

View file

@ -4,12 +4,27 @@ import json
from typing import Dict, Tuple, Union, Optional
from unittest.mock import MagicMock
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from UM.Logger import Logger
from UM.Signal import Signal
class FakeSignal:
def __init__(self):
self._callbacks = []
def connect(self, callback):
self._callbacks.append(callback)
def disconnect(self, callback):
self._callbacks.remove(callback)
def emit(self, *args, **kwargs):
for callback in self._callbacks:
callback(*args, **kwargs)
## This class can be used to mock the QNetworkManager class and test the code using it.
# After patching the QNetworkManager class, requests are prepared before they can be executed.
# Any requests not prepared beforehand will cause KeyErrors.
@ -27,7 +42,7 @@ class NetworkManagerMock:
## Initializes the network manager mock.
def __init__(self) -> None:
# a dict with the prepared replies, using the format {(http_method, url): reply}
self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply]
self.replies = {} # type: Dict[Tuple[str, str], MagicMock]
self.request_bodies = {} # type: Dict[Tuple[str, str], bytes]
# signals used in the network manager.
@ -64,6 +79,8 @@ class NetworkManagerMock:
reply_mock.url().toString.return_value = url
reply_mock.operation.return_value = self._OPERATIONS[method]
reply_mock.attribute.return_value = status_code
reply_mock.finished = FakeSignal()
reply_mock.isFinished.return_value = False
reply_mock.readAll.return_value = response if isinstance(response, bytes) else json.dumps(response).encode()
self.replies[method, url] = reply_mock
Logger.log("i", "Prepared mock {}-response to {} {}", status_code, method, url)
@ -78,6 +95,8 @@ class NetworkManagerMock:
def flushReplies(self) -> None:
for key, reply in self.replies.items():
Logger.log("i", "Flushing reply to {} {}", *key)
reply.isFinished.return_value = True
reply.finished.emit()
self.finished.emit(reply)
self.reset()

View file

@ -8,6 +8,8 @@ from unittest.mock import patch, MagicMock
from cura.CuraApplication import CuraApplication
from src.Cloud.CloudApiClient import CloudApiClient
from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse
from src.Cloud.Models.CloudClusterStatus import CloudClusterStatus
from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse
from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest
from src.Cloud.Models.CloudErrorObject import CloudErrorObject
@ -15,7 +17,6 @@ from tests.Cloud.Fixtures import readFixture, parseFixture
from .NetworkManagerMock import NetworkManagerMock
@patch("cura.NetworkClient.QNetworkAccessManager")
class TestCloudApiClient(TestCase):
def _errorHandler(self, errors: List[CloudErrorObject]):
raise Exception("Received unexpected error: {}".format(errors))
@ -27,15 +28,14 @@ class TestCloudApiClient(TestCase):
self.app = CuraApplication.getInstance()
self.network = NetworkManagerMock()
self.api = CloudApiClient(self.account, self._errorHandler)
def test_GetClusters(self, network_mock):
network_mock.return_value = self.network
with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network):
self.api = CloudApiClient(self.account, self._errorHandler)
def test_getClusters(self):
result = []
with open("{}/Fixtures/getClusters.json".format(os.path.dirname(__file__)), "rb") as f:
response = f.read()
response = readFixture("getClusters")
data = parseFixture("getClusters")["data"]
self.network.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response)
# the callback is a function that adds the result of the call to getClusters to the result list
@ -43,32 +43,26 @@ class TestCloudApiClient(TestCase):
self.network.flushReplies()
self.assertEqual(2, len(result))
def test_getClusterStatus(self, network_mock):
network_mock.return_value = self.network
self.assertEqual([CloudClusterResponse(**data[0]), CloudClusterResponse(**data[1])], result)
def test_getClusterStatus(self):
result = []
with open("{}/Fixtures/getClusterStatusResponse.json".format(os.path.dirname(__file__)), "rb") as f:
response = f.read()
response = readFixture("getClusterStatusResponse")
data = parseFixture("getClusterStatusResponse")["data"]
self.network.prepareReply("GET",
"https://api-staging.ultimaker.com/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status",
200, response
)
self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda status: result.append(status))
self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda s: result.append(s))
self.network.flushReplies()
self.assertEqual(len(result), 1)
status = result[0]
self.assertEqual([CloudClusterStatus(**data)], result)
self.assertEqual(len(status.printers), 2)
self.assertEqual(len(status.print_jobs), 1)
def test_requestUpload(self, network_mock):
network_mock.return_value = self.network
def test_requestUpload(self):
results = []
response = readFixture("putJobUploadResponse")
@ -78,11 +72,11 @@ class TestCloudApiClient(TestCase):
self.api.requestUpload(request, lambda r: results.append(r))
self.network.flushReplies()
self.assertEqual(results[0].content_type, "text/plain")
self.assertEqual(results[0].status, "uploading")
self.assertEqual(["text/plain"], [r.content_type for r in results])
self.assertEqual(["uploading"], [r.status for r in results])
def test_uploadMesh(self, network_mock):
network_mock.return_value = self.network
def test_uploadMesh(self):
results = []
progress = MagicMock()
@ -101,8 +95,8 @@ class TestCloudApiClient(TestCase):
self.assertEqual(["sent"], results)
def test_requestPrint(self, network_mock):
network_mock.return_value = self.network
def test_requestPrint(self):
results = []
response = readFixture("postJobPrintResponse")
@ -120,7 +114,6 @@ class TestCloudApiClient(TestCase):
self.network.flushReplies()
self.assertEqual(len(results), 1)
self.assertEqual(results[0].job_id, job_id)
self.assertEqual(results[0].cluster_job_id, cluster_job_id)
self.assertEqual(results[0].status, "queued")
self.assertEqual([job_id], [r.job_id for r in results])
self.assertEqual([cluster_job_id], [r.cluster_job_id for r in results])
self.assertEqual(["queued"], [r.status for r in results])

View file

@ -13,7 +13,6 @@ from tests.Cloud.Fixtures import readFixture, parseFixture
from .NetworkManagerMock import NetworkManagerMock
@patch("cura.NetworkClient.QNetworkAccessManager")
class TestCloudOutputDevice(TestCase):
CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq"
JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE="
@ -30,7 +29,9 @@ class TestCloudOutputDevice(TestCase):
self.network = NetworkManagerMock()
self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken")
self.onError = MagicMock()
self.device = CloudOutputDevice(CloudApiClient(self.account, self.onError), self.CLUSTER_ID, self.HOST_NAME)
with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network):
self._api = CloudApiClient(self.account, self.onError)
self.device = CloudOutputDevice(self._api, self.CLUSTER_ID, self.HOST_NAME)
self.cluster_status = parseFixture("getClusterStatusResponse")
self.network.prepareReply("GET", self.STATUS_URL, 200, readFixture("getClusterStatusResponse"))
@ -38,8 +39,7 @@ class TestCloudOutputDevice(TestCase):
super().tearDown()
self.network.flushReplies()
def test_status(self, network_mock):
network_mock.return_value = self.network
def test_status(self):
self.device._update()
self.network.flushReplies()
@ -69,32 +69,34 @@ class TestCloudOutputDevice(TestCase):
self.assertEqual({job["name"] for job in self.cluster_status["data"]["print_jobs"]},
{job.name for job in self.device.printJobs})
def test_remove_print_job(self, network_mock):
network_mock.return_value = self.network
def test_remove_print_job(self):
self.device._update()
self.network.flushReplies()
self.assertEqual(1, len(self.device.printJobs))
self.cluster_status["data"]["print_jobs"].clear()
self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status)
self.device._last_response_time = None
self.device._update()
self.network.flushReplies()
self.assertEqual([], self.device.printJobs)
def test_remove_printers(self, network_mock):
network_mock.return_value = self.network
def test_remove_printers(self):
self.device._update()
self.network.flushReplies()
self.assertEqual(2, len(self.device.printers))
self.cluster_status["data"]["printers"].clear()
self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status)
self.device._last_response_time = None
self.device._update()
self.network.flushReplies()
self.assertEqual([], self.device.printers)
@patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack")
def test_print_to_cloud(self, global_container_stack_mock, network_mock):
def test_print_to_cloud(self, global_container_stack_mock):
active_machine_mock = global_container_stack_mock.return_value
active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get
@ -104,7 +106,6 @@ class TestCloudOutputDevice(TestCase):
self.network.prepareReply("PUT", request_upload_response["data"]["upload_url"], 201, b"{}")
self.network.prepareReply("POST", self.PRINT_URL, 200, request_print_response)
network_mock.return_value = self.network
file_handler = MagicMock()
file_handler.getSupportedFileTypesWrite.return_value = [{
"extension": "gcode.gz",

View file

@ -10,7 +10,6 @@ from tests.Cloud.Fixtures import parseFixture, readFixture
from .NetworkManagerMock import NetworkManagerMock
@patch("cura.NetworkClient.QNetworkAccessManager")
class TestCloudOutputDeviceManager(TestCase):
URL = "https://api-staging.ultimaker.com/connect/v1/clusters"
@ -18,13 +17,15 @@ class TestCloudOutputDeviceManager(TestCase):
super().setUp()
self.app = CuraApplication.getInstance()
self.network = NetworkManagerMock()
self.manager = CloudOutputDeviceManager()
with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network):
self.manager = CloudOutputDeviceManager()
self.clusters_response = parseFixture("getClusters")
self.network.prepareReply("GET", self.URL, 200, readFixture("getClusters"))
def tearDown(self):
try:
self._beforeTearDown()
self.manager.stop()
finally:
super().tearDown()
@ -47,17 +48,17 @@ class TestCloudOutputDeviceManager(TestCase):
device_manager.removeOutputDevice(device["cluster_id"])
## Runs the initial request to retrieve the clusters.
def _loadData(self, network_mock):
network_mock.return_value = self.network
self.manager._account.loginStateChanged.emit(True)
self.manager._update_timer.timeout.emit()
def _loadData(self):
self.manager.start()
self.manager._onLoginStateChanged(is_logged_in = True)
self.network.flushReplies()
def test_device_is_created(self, network_mock):
def test_device_is_created(self):
# just create the cluster, it is checked at tearDown
self._loadData(network_mock)
self._loadData()
def test_device_is_updated(self, network_mock):
self._loadData(network_mock)
def test_device_is_updated(self):
self._loadData()
# update the cluster from member variable, which is checked at tearDown
self.clusters_response["data"][0]["host_name"] = "New host name"
@ -65,8 +66,8 @@ class TestCloudOutputDeviceManager(TestCase):
self.manager._update_timer.timeout.emit()
def test_device_is_removed(self, network_mock):
self._loadData(network_mock)
def test_device_is_removed(self):
self._loadData()
# delete the cluster from member variable, which is checked at tearDown
del self.clusters_response["data"][1]
@ -75,41 +76,39 @@ class TestCloudOutputDeviceManager(TestCase):
self.manager._update_timer.timeout.emit()
@patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack")
def test_device_connects_by_cluster_id(self, global_container_stack_mock, network_mock):
def test_device_connects_by_cluster_id(self, global_container_stack_mock):
active_machine_mock = global_container_stack_mock.return_value
cluster1, cluster2 = self.clusters_response["data"]
cluster_id = cluster1["cluster_id"]
active_machine_mock.getMetaDataEntry.side_effect = {"um_cloud_cluster_id": cluster_id}.get
self._loadData(network_mock)
self.network.flushReplies()
self._loadData()
self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected())
self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected())
self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls)
@patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack")
def test_device_connects_by_network_key(self, global_container_stack_mock, network_mock):
def test_device_connects_by_network_key(self, global_container_stack_mock):
active_machine_mock = global_container_stack_mock.return_value
cluster1, cluster2 = self.clusters_response["data"]
network_key = cluster2["host_name"] + ".ultimaker.local"
active_machine_mock.getMetaDataEntry.side_effect = {"um_network_key": network_key}.get
self._loadData(network_mock)
self.network.flushReplies()
self._loadData()
self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected())
self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected())
active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"])
@patch("UM.Message.Message.show")
def test_api_error(self, message_mock, network_mock):
@patch("src.Cloud.CloudOutputDeviceManager.Message")
def test_api_error(self, message_mock):
self.clusters_response = {
"errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}]
}
self.network.prepareReply("GET", self.URL, 200, self.clusters_response)
self._loadData(network_mock)
self.network.flushReplies()
message_mock.assert_called_once_with()
self._loadData()
message_mock.assert_called_once_with(text='Not found!', title='Error', lifetime=10, dismissable=True)
message_mock.return_value.show.assert_called_once_with()