diff --git a/cuegui/cuegui/MainWindow.py b/cuegui/cuegui/MainWindow.py index ccadc9e56..6b81cb307 100644 --- a/cuegui/cuegui/MainWindow.py +++ b/cuegui/cuegui/MainWindow.py @@ -29,6 +29,7 @@ import os import sys import time +import yaml from qtpy import QtCore from qtpy import QtGui @@ -48,6 +49,9 @@ class MainWindow(QtWidgets.QMainWindow): """The main window of the application. Multiple windows may exist.""" + # Message to be displayed when a change requires an application restart + USER_CONFIRM_RESTART = "You must restart for this action to take effect, close window?: " + windows = [] windows_names = [] windows_titles = {} @@ -70,6 +74,7 @@ def __init__(self, app_name, app_version, window_name, parent = None): self.name = window_name else: self.name = self.windows_names[0] + self.__isEnabled = yaml.safe_load(self.app.settings.value("EnableJobInteraction", "False")) # Provides a location for widgets to the right of the menu menuLayout = QtWidgets.QHBoxLayout() @@ -206,6 +211,22 @@ def __createMenus(self): self.windowMenu = self.menuBar().addMenu("&Window") self.helpMenu = self.menuBar().addMenu("&Help") + if self.__isEnabled is False: + # Menu Bar: File -> Enable Job Interaction + enableJobInteraction = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), + '&Enable Job Interaction', self) + enableJobInteraction.setStatusTip('Enable Job Interaction') + enableJobInteraction.triggered.connect(self.__enableJobInteraction) + self.fileMenu.addAction(enableJobInteraction) + # allow user to disable the job interaction + else: + # Menu Bar: File -> Disable Job Interaction + enableJobInteraction = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), + '&Disable Job Interaction', self) + enableJobInteraction.setStatusTip('Disable Job Interaction') + enableJobInteraction.triggered.connect(self.__enableJobInteraction) + self.fileMenu.addAction(enableJobInteraction) + # Menu Bar: File -> Close Window close = QtWidgets.QAction(QtGui.QIcon('icons/exit.png'), '&Close Window', self) close.setStatusTip('Close Window') @@ -464,9 +485,26 @@ def __revertLayout(self): result = QtWidgets.QMessageBox.question( self, "Restart required ", - "You must restart for this action to take effect, close window?: ", + MainWindow.USER_CONFIRM_RESTART, QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if result == QtWidgets.QMessageBox.Yes: self.settings.setValue("RevertLayout", True) self.__windowCloseApplication() + + def __enableJobInteraction(self): + """ Enable/Disable user job interaction """ + result = QtWidgets.QMessageBox.question( + self, + "Job Interaction Settings ", + MainWindow.USER_CONFIRM_RESTART, + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + + if result == QtWidgets.QMessageBox.Yes: + # currently not enabled, user wants to enable + if self.__isEnabled is False: + self.settings.setValue("EnableJobInteraction", 1) + self.__windowCloseApplication() + else: + self.settings.setValue("EnableJobInteraction", 0) + self.__windowCloseApplication() diff --git a/cuegui/cuegui/MenuActions.py b/cuegui/cuegui/MenuActions.py index 7120cf759..14bf267ac 100644 --- a/cuegui/cuegui/MenuActions.py +++ b/cuegui/cuegui/MenuActions.py @@ -80,6 +80,11 @@ class AbstractActions(object): __iconCache = {} + # Template for permission alert messages + USER_INTERACTION_PERMISSIONS = "You do not have permissions to {0} owned by {1}" \ + "\n\nJob actions can still be enabled at File > Enable Job Interaction," \ + " but caution is advised." + def __init__(self, caller, updateCallable, selectedRpcObjectsCallable, sourceCallable): self._caller = caller self.__selectedRpcObjects = selectedRpcObjectsCallable @@ -394,9 +399,22 @@ def kill(self, rpcObjects=None): "The jobs will NOT be able to return once killed.") if cuegui.Utils.questionBoxYesNo(self._caller, "Kill jobs?", msg, [job.data.name for job in jobs]): + blocked_job_owners = [] + authorized_jobs = [] for job in jobs: - job.kill(reason=DEFAULT_JOB_KILL_REASON) - self.killDependents(jobs) + # check permissions + if not cuegui.Utils.isPermissible(job): + blocked_job_owners.append(job.username()) + else: + job.kill(reason=DEFAULT_JOB_KILL_REASON) + authorized_jobs.append(job) + if authorized_jobs: + self.killDependents(authorized_jobs) + if blocked_job_owners: + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "kill some of the selected jobs", + ", ".join(blocked_job_owners))) self._update() def killDependents(self, jobs): @@ -466,8 +484,18 @@ def eatDead(self, rpcObjects=None): if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", "Eat all DEAD frames in selected jobs?", [job.data.name for job in jobs]): + blocked_job_owners = [] for job in jobs: - job.eatFrames(state=[opencue.compiled_proto.job_pb2.DEAD]) + # check permissions + if not cuegui.Utils.isPermissible(job): + blocked_job_owners.append(job.username()) + else: + job.eatFrames(state=[opencue.compiled_proto.job_pb2.DEAD]) + if blocked_job_owners: + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "eat dead for some of the selected jobs", + ", ".join(blocked_job_owners))) self._update() autoEatOn_info = ["Enable auto eating", None, "eat"] @@ -475,9 +503,18 @@ def eatDead(self, rpcObjects=None): def autoEatOn(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: + blocked_job_owners = [] for job in jobs: - job.setAutoEat(True) - job.eatFrames(state=[opencue.compiled_proto.job_pb2.DEAD]) + if not cuegui.Utils.isPermissible(job): + blocked_job_owners.append(job.username()) + else: + job.setAutoEat(True) + job.eatFrames(state=[opencue.compiled_proto.job_pb2.DEAD]) + if blocked_job_owners: + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "enable auto eating frames", + ", ".join(blocked_job_owners))) self._update() autoEatOff_info = ["Disable auto eating", None, "eat"] @@ -485,8 +522,17 @@ def autoEatOn(self, rpcObjects=None): def autoEatOff(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: + blocked_job_owners = [] for job in jobs: - job.setAutoEat(False) + if not cuegui.Utils.isPermissible(job): + blocked_job_owners.append(job.username()) + else: + job.setAutoEat(False) + if blocked_job_owners: + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "disable auto eating frames", + ", ".join(blocked_job_owners))) self._update() retryDead_info = ["Retry dead frames", None, "retry"] @@ -494,12 +540,22 @@ def autoEatOff(self, rpcObjects=None): def retryDead(self, rpcObjects=None): jobs = self._getOnlyJobObjects(rpcObjects) if jobs: + # check permissions if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", "Retry all DEAD frames in selected jobs?", [job.data.name for job in jobs]): + blocked_job_owners = [] for job in jobs: - job.retryFrames( - state=[opencue.compiled_proto.job_pb2.DEAD]) + if not cuegui.Utils.isPermissible(job): + blocked_job_owners.append(job.username()) + else: + job.retryFrames( + state=[opencue.compiled_proto.job_pb2.DEAD]) + if blocked_job_owners: + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "retry dead for some of the selected jobs", + ", ".join(blocked_job_owners))) self._update() dropExternalDependencies_info = ["Drop External Dependencies", None, "kill"] @@ -792,48 +848,76 @@ def setTags(self, rpcObjects=None): def kill(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Kill ALL frames in selected layers?", - [layer.data.name for layer in layers]): - for layer in layers: - layer.kill(reason=DEFAULT_FRAME_KILL_REASON) - self._update() + #check permissions + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "kill layers", + self._getSource().username())) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Kill ALL frames in selected layers?", + [layer.data.name for layer in layers]): + for layer in layers: + layer.kill(reason=DEFAULT_FRAME_KILL_REASON) + self._update() eat_info = ["&Eat", None, "eat"] def eat(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Eat ALL frames in selected layers?", - [layer.data.name for layer in layers]): - for layer in layers: - layer.eat() - self._update() + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "eat layers", + self._getSource().username()) + ) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Eat ALL frames in selected layers?", + [layer.data.name for layer in layers]): + for layer in layers: + layer.eat() + self._update() retry_info = ["&Retry", None, "retry"] def retry(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Retry ALL frames in selected layers?", - [layer.data.name for layer in layers]): - for layer in layers: - layer.retry() - self._update() + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "retry layers", + self._getSource().username()) + ) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Retry ALL frames in selected layers?", + [layer.data.name for layer in layers]): + for layer in layers: + layer.retry() + self._update() retryDead_info = ["Retry dead frames", None, "retry"] def retryDead(self, rpcObjects=None): layers = self._getOnlyLayerObjects(rpcObjects) if layers: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Retry all DEAD frames in selected layers?", - [layer.data.name for layer in layers]): - layers[-1].parent().retryFrames(layer=[layer.data.name for layer in layers], - state=[opencue.api.job_pb2.DEAD]) - self._update() + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "retry dead layers", + self._getSource().username()) + ) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Retry all DEAD frames in selected layers?", + [layer.data.name for layer in layers]): + layers[-1].parent().retryFrames(layer=[layer.data.name for layer in layers], + state=[opencue.api.job_pb2.DEAD]) + self._update() markdone_info = ["Mark done", None, "markdone"] @@ -1068,10 +1152,18 @@ def retry(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: job = self._getSource() - if cuegui.Utils.questionBoxYesNo( - self._caller, "Confirm", "Retry selected frames?", names): - job.retryFrames(name=names) - self._update() + # check permissions + if not cuegui.Utils.isPermissible(job): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "retry frames", + job.username()) + ) + else: + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Retry selected frames?", names): + job.retryFrames(name=names) + self._update() previewMain_info = ["Preview Main", None, "previewMain"] @@ -1105,23 +1197,36 @@ def previewAovs(self, rpcObjects=None): def eat(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Eat selected frames?", - names): - self._getSource().eatFrames(name=names) - self._update() + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "eat frames", + self._getSource().username()) + ) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Eat selected frames?", + names): + self._getSource().eatFrames(name=names) + self._update() kill_info = ["&Kill", None, "kill"] def kill(self, rpcObjects=None): names = [frame.data.name for frame in self._getOnlyFrameObjects(rpcObjects)] if names: - if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", - "Kill selected frames?", - names): - self._getSource().killFrames(reason=DEFAULT_FRAME_KILL_REASON, - name=names) - self._update() + if not cuegui.Utils.isPermissible(self._getSource(), self): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "kill frames", + self._getSource().username())) + else: + if cuegui.Utils.questionBoxYesNo(self._caller, "Confirm", + "Kill selected frames?", + names): + self._getSource().killFrames(reason=DEFAULT_FRAME_KILL_REASON, + name=names) + self._update() markAsWaiting_info = ["Mark as &waiting", None, "configure"] @@ -1225,8 +1330,15 @@ def eatandmarkdone(self, rpcObjects=None): frames = self._getOnlyFrameObjects(rpcObjects) if frames: frameNames = [frame.data.name for frame in frames] - - if cuegui.Utils.questionBoxYesNo( + #check permissions + if not cuegui.Utils.isPermissible(self._getSource(), self): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "eat and mark done frames", + self._getSource().username()) + ) + return + if not cuegui.Utils.questionBoxYesNo( self._caller, "Confirm", "Eat and Mark done all selected frames?\n" "(Drops any dependencies that are waiting on these frames)\n\n" @@ -1234,38 +1346,39 @@ def eatandmarkdone(self, rpcObjects=None): "eaten or succeeded frames, any dependencies on the\n" "layer will be dropped as well.", frameNames): + return - # Mark done the layers to drop their dependencies if the layer is done - - if len(frames) == 1: - # Since only a single frame selected, check if layer is only one frame - layer = opencue.api.findLayer(self._getSource().data.name, - frames[0].data.layer_name) - if layer.data.layer_stats.total_frames == 1: - # Single frame selected of single frame layer, mark done and eat it all - layer.eat() - layer.markdone() + # Mark done the layers to drop their dependencies if the layer is done - self._update() - return + if len(frames) == 1: + # Since only a single frame selected, check if layer is only one frame + layer = opencue.api.findLayer(self._getSource().data.name, + frames[0].data.layer_name) + if layer.data.layer_stats.total_frames == 1: + # Single frame selected of single frame layer, mark done and eat it all + layer.eat() + layer.markdone() - self._getSource().eatFrames(name=frameNames) - self._getSource().markdoneFrames(name=frameNames) + self._update() + return - # Warning: The below assumes that eaten frames are desired to be markdone - - # Wait for the markDoneFrames to be processed, then drop the dependencies on - # the layer if all frames are done. - layerNames = [frame.data.layer_name for frame in frames] - time.sleep(1) - for layer in self._getSource().getLayers(): - if layer.data.name in layerNames: - if ( - layer.data.layer_stats.eaten_frames + - layer.data.layer_stats.succeeded_frames == - layer.data.layer_stats.total_frames): - layer.markdone() - self._update() + self._getSource().eatFrames(name=frameNames) + self._getSource().markdoneFrames(name=frameNames) + + # Warning: The below assumes that eaten frames are desired to be markdone + + # Wait for the markDoneFrames to be processed, then drop the dependencies on + # the layer if all frames are done. + layerNames = [frame.data.layer_name for frame in frames] + time.sleep(1) + for layer in self._getSource().getLayers(): + if layer.data.name in layerNames: + if ( + layer.data.layer_stats.eaten_frames + + layer.data.layer_stats.succeeded_frames == + layer.data.layer_stats.total_frames): + layer.markdone() + self._update() class ShowActions(AbstractActions): @@ -1698,14 +1811,24 @@ def view(self, rpcObjects=None): def kill(self, rpcObjects=None): procs = self._getOnlyProcObjects(rpcObjects) if procs: - if cuegui.Utils.questionBoxYesNo( - self._caller, "Confirm", "Kill selected frames?", - ["%s -> %s @ %s" % (proc.data.job_name, proc.data.frame_name, proc.data.name) - for proc in procs]): - for proc in procs: - self.cuebotCall(proc.kill, - "Kill Proc %s Failed" % proc.data.name) - self._update() + if not cuegui.Utils.isPermissible(self._getSource()): + cuegui.Utils.showErrorMessageBox( + AbstractActions.USER_INTERACTION_PERMISSIONS.format( + "eat and mark done frames", + self._getSource().username()) + ) + else: + if cuegui.Utils.questionBoxYesNo( + self._caller, "Confirm", "Kill selected frames?", + ["%s -> %s @ %s" % ( + proc.data.job_name, + proc.data.frame_name, + proc.data.name) + for proc in procs]): + for proc in procs: + self.cuebotCall(proc.kill, + "Kill Proc %s Failed" % proc.data.name) + self._update() unbook_info = ["Unbook", None, "eject"] diff --git a/cuegui/cuegui/Utils.py b/cuegui/cuegui/Utils.py index 30e5a3754..fb425a246 100644 --- a/cuegui/cuegui/Utils.py +++ b/cuegui/cuegui/Utils.py @@ -31,6 +31,7 @@ import time import traceback import webbrowser +import yaml from qtpy import QtCore from qtpy import QtGui @@ -877,3 +878,21 @@ def byteConversion(amount, btype): for _ in range(n): _bytes *= 1024 return _bytes + + +def isPermissible(jobObject): + """ + Validate if the current user has the correct permissions to perform + the action + + :param userName: jobObject + :ptype userName: Opencue Job Object + :return: + """ + # Read cached setting from user config file + hasPermissions = yaml.safe_load(cuegui.app().settings.value("EnableJobInteraction", "False")) + # If not set by default, check if current user is the job owner + currentUser = getpass.getuser() + if not hasPermissions and currentUser.lower() == jobObject.username().lower(): + hasPermissions = True + return hasPermissions diff --git a/cuegui/tests/MenuActions_tests.py b/cuegui/tests/MenuActions_tests.py index 7c116b62c..9bf3a74ed 100644 --- a/cuegui/tests/MenuActions_tests.py +++ b/cuegui/tests/MenuActions_tests.py @@ -230,7 +230,8 @@ def test_resume(self): job.resume.assert_called() @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_kill(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_kill(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.kill = mock.Mock() job.getWhatDependsOnThis = mock.Mock() @@ -241,7 +242,8 @@ def test_kill(self, yesNoMock): job.kill.assert_called() @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_killCanceled(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_killCanceled(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.kill = mock.Mock() @@ -250,7 +252,8 @@ def test_killCanceled(self, yesNoMock): job.kill.assert_not_called() @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_eatDead(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eatDead(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.eatFrames = mock.Mock() @@ -259,7 +262,8 @@ def test_eatDead(self, yesNoMock): job.eatFrames.assert_called_with(state=[opencue.compiled_proto.job_pb2.DEAD]) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_eatDeadCanceled(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eatDeadCanceled(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.eatFrames = mock.Mock() @@ -267,7 +271,8 @@ def test_eatDeadCanceled(self, yesNoMock): job.eatFrames.assert_not_called() - def test_autoEatOn(self): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_autoEatOn(self, isPermissibleMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.setAutoEat = mock.Mock() job.eatFrames = mock.Mock() @@ -277,7 +282,8 @@ def test_autoEatOn(self): job.setAutoEat.assert_called_with(True) job.eatFrames.assert_called_with(state=[opencue.compiled_proto.job_pb2.DEAD]) - def test_autoEatOff(self): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_autoEatOff(self, isPermissibleMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.setAutoEat = mock.Mock() @@ -286,7 +292,8 @@ def test_autoEatOff(self): job.setAutoEat.assert_called_with(False) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_retryDead(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retryDead(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.retryFrames = mock.Mock() @@ -295,7 +302,8 @@ def test_retryDead(self, yesNoMock): job.retryFrames.assert_called_with(state=[opencue.compiled_proto.job_pb2.DEAD]) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_retryDeadCanceled(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retryDeadCanceled(self, isPermissibleMock, yesNoMock): job = opencue.wrappers.job.Job(opencue.compiled_proto.job_pb2.Job(name='job-name')) job.retryFrames = mock.Mock() @@ -669,7 +677,8 @@ def test_setTags(self, layerTagsDialogMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'kill') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_kill(self, yesNoMock, killMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_kill(self, isPermissibleMock, yesNoMock, killMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -679,7 +688,8 @@ def test_kill(self, yesNoMock, killMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'kill') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_killCanceled(self, yesNoMock, killMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_killCanceled(self, isPermissibleMock, yesNoMock, killMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -689,7 +699,8 @@ def test_killCanceled(self, yesNoMock, killMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'eat') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_eat(self, yesNoMock, eatMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eat(self, isPermissibleMock, yesNoMock, eatMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -699,7 +710,8 @@ def test_eat(self, yesNoMock, eatMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'eat') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_eatCanceled(self, yesNoMock, eatMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eatCanceled(self, isPermissibleMock, yesNoMock, eatMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -709,7 +721,8 @@ def test_eatCanceled(self, yesNoMock, eatMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'retry', autospec=True) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_retry(self, yesNoMock, retryMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retry(self, isPermissibleMock, yesNoMock, retryMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -719,7 +732,8 @@ def test_retry(self, yesNoMock, retryMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'retry') @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=False) - def test_retryCanceled(self, yesNoMock, retryMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retryCanceled(self, isPermissibleMock, yesNoMock, retryMock): layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name='arbitrary-name')) @@ -728,7 +742,8 @@ def test_retryCanceled(self, yesNoMock, retryMock): retryMock.assert_not_called() @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_retryDead(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retryDead(self, isPermissibleMock, yesNoMock): layer_name = 'arbitrary-name' layer = opencue.wrappers.layer.Layer( opencue.compiled_proto.job_pb2.Layer(name=layer_name)) @@ -914,7 +929,8 @@ def test_getWhatDependsOnThis(self): self.frame_actions.getWhatDependsOnThis(rpcObjects=[frame]) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_retry(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_retry(self, isPermissibleMock, yesNoMock): frame_name = 'arbitrary-frame-name' frame = opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(name=frame_name)) @@ -943,7 +959,8 @@ def test_previewAovs(self, previewProcessorDialogMock): previewProcessorDialogMock.return_value.exec_.assert_called() @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_eat(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eat(self, isPermissibleMock, yesNoMock): frame_name = 'arbitrary-frame-name' frame = opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(name=frame_name)) @@ -952,7 +969,8 @@ def test_eat(self, yesNoMock): self.job.eatFrames.assert_called_with(name=[frame_name]) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_kill(self, yesNoMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_kill(self, isPermissibleMock, yesNoMock): frame_name = 'arbitrary-frame-name' frame = opencue.wrappers.frame.Frame(opencue.compiled_proto.job_pb2.Frame(name=frame_name)) @@ -1019,7 +1037,8 @@ def test_copyLogFileName(self, getFrameLogFileMock, clipboardMock): @mock.patch.object(opencue.wrappers.layer.Layer, 'markdone', autospec=True) @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) - def test_eatandmarkdone(self, yesNoMock, markdoneMock): + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_eatandmarkdone(self, isPermissibleMock, yesNoMock, markdoneMock): layer_name = 'layer-name' frames = [ opencue.wrappers.frame.Frame( @@ -1339,7 +1358,7 @@ def setUp(self): self.app = test_utils.createApplication() self.widgetMock = mock.Mock() self.proc_actions = cuegui.MenuActions.ProcActions( - self.widgetMock, mock.Mock(), None, None) + self.widgetMock, mock.Mock(), mock.Mock(), mock.Mock()) @mock.patch('opencue.api.findJob') def test_view(self, findJobMock): @@ -1353,8 +1372,9 @@ def test_view(self, findJobMock): self.app.view_object.emit.assert_called_once_with(job) - @mock.patch('cuegui.Utils.questionBoxYesNo', new=mock.Mock(return_value=True)) - def test_kill(self): + @mock.patch('cuegui.Utils.questionBoxYesNo', return_value=True) + @mock.patch('cuegui.Utils.isPermissible', return_value=True) + def test_kill(self, isPermissibleMock, yesNoMock): proc = opencue.wrappers.proc.Proc(opencue.compiled_proto.host_pb2.Proc()) proc.kill = mock.MagicMock()