def test_01_version_increment(self, test): """ Activations result in a Device version number increment""" globalConfig.test = test devicesWithAdvertisements = self.find_device_advertisement() versionNumbersBeforeActivation = [] for device in devicesWithAdvertisements: versionNumbersBeforeActivation.append(device['version']) output = getOutputList()[0] action = output.findAcceptableTestRoute() activation = Activation() activation.addAction(action) activation.fireActivation() versionIncremented = False counter = 0 devicesWithAdvertisements = self.find_device_advertisement() for device in devicesWithAdvertisements: if device['version'] != versionNumbersBeforeActivation[counter]: versionIncremented = True if versionIncremented: return test.PASS() else: return test.FAIL( "No devices in the Node API incremented version number on activation." )
def check_delayed_activation(self, activationTime, activationType): active = Active() active.unrouteAll() preActivationState = active.buildJSONObject() outputList = getOutputList() testRouteAction = outputList[0].findAcceptableTestRoute() activation = Activation() activation.addAction(testRouteAction) activation.type = activationType activation.activationTimestamp = activationTime try: activation.fireActivation() except NMOSTestException as e: time.sleep(2) raise e pendingState = active.buildJSONObject() if not compare_json(preActivationState, pendingState): msg = globalConfig.test.FAIL("Scheduled Activation completed immediately") raise NMOSTestException(msg) time.sleep(2) active.assertActionCompleted(testRouteAction, retries=5) time.sleep(1)
def test_08_no_reentrant_loops(self, test): """If the device allows re-entrant matrices, the constraints are set such that it is not possible to create a loop""" globalConfig.test = test forbiddenRoutes = [] outputList = getOutputList() inputList = getInputList() for outputInstance in outputList: sourceID = outputInstance.getSourceID() for inputInstance in inputList: inputParent = inputInstance.getParent() if sourceID == inputParent: route = { "input": inputInstance, "output": outputInstance } forbiddenRoutes.append(route) for route in forbiddenRoutes: outputCaps = route['output'].getCaps() msg = ("It is possible to create a loop using re-entrant matricies" " between input {} and output {}".format(route['input'].id, route['output'].id)) try: routableInputs = outputCaps['routable_inputs'] except KeyError: return test.FAIL(msg) if route['output'].id not in routableInputs: return test.FAIL(msg) return test.PASS()
def test_07_unrouted_channels_null(self, test): """Channels in the active resource where no input channel is routed have `null` set as the `input` and `channel_index`""" globalConfig.test = test activeInstance = Active() outputList = getOutputList() for outputInstance in outputList: channelList = outputInstance.getChannelList() for channelID in range(0, len(channelList)): inputChannelIndex = activeInstance.getInputChannelIndex( outputInstance, channelID ) inputChannelName = activeInstance.getInputChannelName( outputInstance, channelID ) if inputChannelIndex is None or inputChannelName is None: if inputChannelIndex != inputChannelName: msg = ("Both the channel index and name must be set" " to `null` when the a channel is not routed") test.FAIL(msg) return test.PASS()
def getRoutableOutputs(self): routableOutputList = [] for outputInstance in getOutputList(): if self.getBlockSize() <= len(outputInstance.getChannelList()): if self.id in outputInstance.getRoutableInputList(): routableOutputList.append(outputInstance) return routableOutputList
def unrouteAll(self): outputList = getOutputList() activation = Activation() for outputInstance in outputList: for channelID in range(0, len(outputInstance.getChannelList())): action = Action(None, outputInstance.id, None, channelID) activation.addAction(action) activation.fireActivation()
def test_12_outputs_have_channels(self, test): """Outputs have at least one channel represented in their channels resource""" globalConfig.test = test outputList = getOutputList() for outputInstance in outputList: channels = outputInstance.getChannelList() if len(channels) == 0: return test.FAIL("Outputs must have at least one channel") return test.PASS()
def test_02_immediate_activation(self, test): """Immediate activation can be called on the API""" globalConfig.test = test outputList = getOutputList() testRouteAction = outputList[0].findAcceptableTestRoute() activation = Activation() activation.addAction(testRouteAction) activation.fireActivation() activeResource = Active() activeResource.assertActionCompleted(testRouteAction) return test.PASS()
def test_06_locking_response(self, test): """Attempting to change a locked route results in a 423 response""" globalConfig.test = test outputList = getOutputList() testRouteAction = outputList[0].findAcceptableTestRoute() activation = Activation() activation.addAction(testRouteAction) activation.type = SCHEDULED_RELATIVE_ACTIVATION activation.activationTimestamp = "5:0" activation.fireActivation() activation.checkLock() return test.PASS()
def test_13_violate_routing_constraints_rejected(self, test): """Attempting to violate routing constraints results in an HTTP 400 response""" globalConfig.test = test outputList = getOutputList() constrainedOutputList = [] for outputInstance in outputList: constraints = outputInstance.getCaps() try: routableInputs = constraints['routable_inputs'] except KeyError: pass else: constrainedOutputList.append({ "output": outputInstance, "routableInputs": routableInputs }) if len(constrainedOutputList) == 0: return test.NA( "Could not test - no outputs have routing constraints set.") inputList = getInputList() inputIDList = [] for inputInstance in inputList: inputIDList.append(inputInstance.id) for constrainedOutput in constrainedOutputList: forbiddenRoutes = copy.deepcopy(inputIDList) for routableInputID in constrainedOutput['routableInputs']: forbiddenRoutes.remove(routableInputID) if len(forbiddenRoutes) > 0: action = Action(forbiddenRoutes[0], constrainedOutput['output'].id) activation = Activation() activation.addAction(action) try: activation.checkReject() return test.PASS() except NMOSTestException: msg = ( "Was able to create a forbidden route between input {}" " and output {} despite routing constraint." "".format(forbiddenRoutes[0], outputInstance.id)) return test.FAIL(msg) return test.NA("Could not test - no route is forbidden.")
def test_03_source_ids_in_is04(self, test): """ All Output Source IDs match up to the IS-04 Node API""" globalConfig.test = test outputList = getOutputList() allSourcesRegistered = True for outputInstance in outputList: sourceRegistered = False sourceID = outputInstance.getSourceID() if sourceID is None: sourceRegistered = True else: sourceRegistered = self.findSourceID(sourceID) if not sourceRegistered: allSourcesRegistered = False if allSourcesRegistered: return test.PASS() else: return test.FAIL( "Not all Output sources IDs were advertised in the Node API")
def test_01_io_content_match(self, test): """Content of the /io view matches resources elsewhere in the API""" globalConfig.test = test inputList = getInputList() outputList = getOutputList() ioInstance = IO() ioJSON = ioInstance.getIOAsJSON() mockIoResource = {"inputs": {}, "outputs": {}} for input in inputList: mockIoResource["inputs"][input.id] = input.assembleInputObject() for output in outputList: mockIoResource["outputs"][output.id] = output.assembleOutputObject() if compare_json(mockIoResource, ioJSON): return test.PASS() else: return test.FAIL("IO Resource does not correctly reflect the API resources")
def test_05_delete_activations(self, test): """Activations can be deleted once created""" globalConfig.test = test Active().unrouteAll() outputList = getOutputList() testRouteAction = outputList[0].findAcceptableTestRoute() activation = Activation() activation.addAction(testRouteAction) activation.type = SCHEDULED_RELATIVE_ACTIVATION activation.activationTimestamp = "2:0" try: activation.fireActivation() except NMOSTestException as e: time.sleep(2) raise e time.sleep(1) activation.delete() return test.PASS()