Commit ef6e6fcf authored by Jaden's avatar Jaden Committed by Samuel GAIST

[schema][algorithm] refactor asserts & add range num handling

The schema now limits the range entries according to the
specific numerical type (uint/int/float).
Also refactors out all the nose.tools.assert_true/false calls.
parent 3ef6c1c1
......@@ -64,7 +64,21 @@
"minItems": 1
},
"range": {
"uint_range": {
"type": "array",
"items": { "type": "integer", "minimum": 0 },
"minItems": 2,
"maxItems": 2
},
"int_range": {
"type": "array",
"items": { "type": "integer" },
"minItems": 2,
"maxItems": 2
},
"float_range": {
"type": "array",
"items": { "type": "number" },
"minItems": 2,
......@@ -96,7 +110,7 @@
"type": { "$ref": "../common/1.json#/definitions/basetype" },
"default": { "$ref": "../common/1.json#/definitions/value" },
"description": { "type": "string" },
"range": { "$ref": "#/definitions/range" }
"range": { "$ref": "#/definitions/float_range" }
},
"required": [
"type",
......@@ -113,6 +127,28 @@
}
}
},
"allOf": [
{
"if": { "properties": { "type": {
"type": "string", "enum": [
"uint8",
"uint16",
"uint32",
"uint64"
] } } },
"then": { "properties": { "range": { "$ref": "#/definitions/uint_range" } } }
},
{
"if": { "properties": { "type": {
"type": "string", "enum": [
"int8",
"int16",
"int32",
"int64"
] } } },
"then": { "properties": { "range": { "$ref": "#/definitions/int_range" } } }
}
],
"additionalProperties": false
},
......
{
"api_version": 2,
"schema_version": 3,
"splittable": false,
"type": "sequential",
"uses": {},
"description": "this is an example of an algorithm with an invalid range parameter (uints must have integer range values)",
"groups": [
{
"name": "group",
"inputs": {
"input": {
"type": "user/single_integer/1"
}
},
"outputs": {
"output": {
"type": "user/single_integer/1"
}
}
}
],
"language": "python",
"parameters": {
"testparam": {
"range": [
0.1,
1.1
],
"type": "uint8"
}
}
}
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
class Algorithm:
def process(self, inputs, outputs):
return True
{
"api_version": 2,
"schema_version": 3,
"splittable": false,
"type": "sequential",
"uses": {},
"description": "this is an example of an algorithm with an invalid range parameter (uints must have non-negative range values)",
"groups": [
{
"name": "group",
"inputs": {
"input": {
"type": "user/single_integer/1"
}
},
"outputs": {
"output": {
"type": "user/single_integer/1"
}
}
}
],
"language": "python",
"parameters": {
"testparam": {
"range": [
-1,
0
],
"type": "uint8"
}
}
}
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
class Algorithm:
def process(self, inputs, outputs):
return True
{
"api_version": 2,
"schema_version": 3,
"splittable": false,
"type": "sequential",
"uses": {},
"description": "this is an example of an algorithm with an invalid range parameter (ints must have integer range values)",
"groups": [
{
"name": "group",
"inputs": {
"input": {
"type": "user/single_integer/1"
}
},
"outputs": {
"output": {
"type": "user/single_integer/1"
}
}
}
],
"language": "python",
"parameters": {
"testparam": {
"range": [
0.1,
1.1
],
"type": "int8"
}
}
}
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
class Algorithm:
def process(self, inputs, outputs):
return True
{
"api_version": 2,
"schema_version": 3,
"splittable": false,
"type": "sequential",
"uses": {},
"description": "this is an example of an algorithm with a valid range parameter",
"groups": [
{
"name": "group",
"inputs": {
"input": {
"type": "user/single_integer/1"
}
},
"outputs": {
"output": {
"type": "user/single_integer/1"
}
}
}
],
"language": "python",
"parameters": {
"testparam": {
"range": [
0.1,
1.1
],
"type": "float64"
}
}
}
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
class Algorithm:
def __init__(self):
pass
def setup(self, parameters):
return True
def process(self, inputs, outputs):
return True
{
"api_version": 2,
"schema_version": 3,
"splittable": false,
"type": "sequential",
"uses": {},
"description": "this is an example of an algorithm with a valid range parameter",
"groups": [
{
"name": "group",
"inputs": {
"input": {
"type": "user/single_integer/1"
}
},
"outputs": {
"output": {
"type": "user/single_integer/1"
}
}
}
],
"language": "python",
"parameters": {
"testparam": {
"range": [
-1,
1
],
"type": "int8"
}
}
}
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
class Algorithm:
def __init__(self):
pass
def setup(self, parameters):
return True
def process(self, inputs, outputs):
return True
......@@ -41,16 +41,25 @@ from ..algorithm import Algorithm
from . import prefix, tmp_prefix
from .utils import cleanup
def alg_assert_valid(algorithm):
"""Helper to assert that the given algorithm is valid,
otherwise fail and print the algorithm's errors"""
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
def alg_assert_invalid(algorithm):
"""Helper to assert that the given algorithm is invalid"""
nose.tools.assert_false(algorithm.valid)
# ----------------------------------------------------------
def test_load_default_algorithm():
algorithm = Algorithm(prefix, data=None)
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
alg_assert_valid(Algorithm(prefix, data=None))
# ----------------------------------------------------------
......@@ -59,7 +68,7 @@ def test_load_default_algorithm():
def test_missing_inputs():
algorithm = Algorithm(prefix, "errors/no_inputs_declarations/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
nose.tools.assert_not_equal(
algorithm.errors[0].find("'inputs' is a required property"), -1
)
......@@ -71,7 +80,7 @@ def test_missing_inputs():
def test_missing_outputs():
algorithm = Algorithm(prefix, "errors/no_outputs_declarations/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
nose.tools.assert_not_equal(
algorithm.errors[0].find("'outputs' is a required property"), -1
)
......@@ -83,7 +92,7 @@ def test_missing_outputs():
def test_invalid_loop_channel():
algorithm = Algorithm(prefix, "schema/invalid_loop_channel/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
nose.tools.assert_not_equal(
algorithm.errors[0].find("'request' is a required property"), -1
)
......@@ -105,107 +114,115 @@ def test_duplicate_key():
def test_v2():
algorithm = Algorithm(prefix, "user/integers_add_v2/1")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
alg_assert_valid(algorithm)
def test_analyzer_v2():
algorithm = Algorithm(prefix, "user/integers_echo_analyzer_v2/1")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
alg_assert_valid(algorithm)
def test_v3():
algorithm = Algorithm(prefix, "autonomous/loop/1")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
alg_assert_valid(algorithm)
algorithm = Algorithm(prefix, "autonomous/loop_user/1")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
alg_assert_valid(algorithm)
def test_invalid_v3():
algorithm = Algorithm(prefix, "schema/invalid_loop_output/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
algorithm = Algorithm(prefix, "schema/invalid_loop_type/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
algorithm = Algorithm(prefix, "schema/invalid_loop_user_type/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# ----------------------------------------------------------
def test_valid_range_parameter():
algorithm = Algorithm(prefix, "user/valid_parameter/1")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
def test_valid_range_parameters():
# float
algorithm = Algorithm(prefix, "user/valid_range_parameter/1")
alg_assert_valid(algorithm)
# uint
algorithm = Algorithm(prefix, "user/valid_range_parameter/2")
alg_assert_valid(algorithm)
# int
algorithm = Algorithm(prefix, "user/valid_range_parameter/3")
alg_assert_valid(algorithm)
def test_valid_choice_parameters():
algorithm = Algorithm(prefix, "user/valid_parameter/2")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
algorithm = Algorithm(prefix, "user/valid_choice_parameter/1")
alg_assert_valid(algorithm)
algorithm = Algorithm(prefix, "user/valid_parameter/3")
nose.tools.assert_true(
algorithm.valid, "\n * %s" % "\n * ".join(algorithm.errors)
)
algorithm = Algorithm(prefix, "user/valid_choice_parameter/2")
alg_assert_valid(algorithm)
def test_invalid_range_parameters():
# range params cant have type: "string"
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/2")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/3")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/4")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# cant have a "range" and "choice" field
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/5")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# uints needs integer ranges
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/6")
alg_assert_invalid(algorithm)
# uints needs non-negative ranges
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/7")
alg_assert_invalid(algorithm)
# ints needs integer ranges
algorithm = Algorithm(prefix, "schema/invalid_range_parameter/8")
alg_assert_invalid(algorithm)
def test_invalid_choice_parameters():
# needs >=3 choices
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/1")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# all choices must be the same type
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/2")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# numerical types must have numerical choices
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/3")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# string types must have string choices
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/4")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# cannot be boolean type
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/5")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# needs >=3 choices
algorithm = Algorithm(prefix, "schema/invalid_choice_parameter/6")
nose.tools.assert_false(algorithm.valid)
alg_assert_invalid(algorithm)
# ----------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment