aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilo Casagrande <milo.casagrande@linaro.org>2015-03-09 16:39:30 +0100
committerMilo Casagrande <milo.casagrande@linaro.org>2015-03-09 16:39:30 +0100
commit66a8ee0375f945fb14c8fcc7025f2dcac9cc977c (patch)
tree278c3aec1f2d7ef4a422d0cd89279422b44ebcf3
parent3def8cddddad5e63c5873f8d0ec53afaf0c980c9 (diff)
Add test case model and tests.
-rw-r--r--app/models/__init__.py17
-rw-r--r--app/models/test_case.py276
-rw-r--r--app/models/tests/test_test_case_model.py269
-rw-r--r--app/tests/__init__.py3
4 files changed, 564 insertions, 1 deletions
diff --git a/app/models/__init__.py b/app/models/__init__.py
index 1b31cbf..c69d1e6 100644
--- a/app/models/__init__.py
+++ b/app/models/__init__.py
@@ -26,6 +26,7 @@ AGGREGATE_KEY = "aggregate"
ARCHITECTURE_KEY = "arch"
ARM64_ARCHITECTURE_KEY = "arm64"
ARM_ARCHITECTURE_KEY = "arm"
+ATTACHMENTS_KEY = "attachments"
BOARD_INSTANCE_KEY = "board_instance"
BOARD_KEY = "board"
BOOT_ID_KEY = "boot_id"
@@ -86,6 +87,7 @@ KCONFIG_FRAGMENTS_KEY = "kconfig_fragments"
KERNEL_CONFIG_KEY = "kernel_config"
KERNEL_IMAGE_KEY = "kernel_image"
KERNEL_KEY = "kernel"
+KVM_GUEST_KEY = "kvm_guest"
LAB_ID_KEY = "lab_id"
LAB_NAME_KEY = "lab_name"
LIMIT_KEY = "limit"
@@ -93,7 +95,10 @@ LOAD_ADDR_KEY = "load_addr"
LT_KEY = "lt"
MACH_KEY = "mach"
MANDATORY_KEYS = "mandatory"
+MAXIMUM_KEY = "maximum"
+MEASUREMENTS_KEY = "measurements"
METADATA_KEY = "metadata"
+MINIMUM_KEY = "minimum"
MODULES_DIR_KEY = "modules_dir"
MODULES_KEY = "modules"
NAME_KEY = "name"
@@ -105,6 +110,9 @@ QEMU_COMMAND_KEY = "qemu_command"
QEMU_KEY = "qemu"
RESULT_KEY = "result"
RETRIES_KEY = "retries"
+SAMPLES_KEY = "samples"
+SAMPLES_SQUARE_SUM_KEY = "samples_sqr_sum"
+SAMPLES_SUM_KEY = "samples_sum"
SKIP_KEY = "skip"
SORT_KEY = "sort"
SORT_ORDER_KEY = "sort_order"
@@ -159,6 +167,7 @@ PASS_STATUS = "PASS"
SENT_STATUS = "SENT"
UNKNOWN_STATUS = "UNKNOWN"
UNTRIED_STATUS = "UNTRIED"
+SKIP_STATUS = "SKIP"
# Build file names.
DONE_FILE = ".done"
@@ -247,6 +256,14 @@ VALID_JOB_STATUS = [
UNKNOWN_STATUS,
]
+# Valid test case status.
+VALID_TEST_CASE_STATUS = [
+ ERROR_STATUS,
+ FAIL_STATUS,
+ PASS_STATUS,
+ SKIP_STATUS
+]
+
# The valid collections for the bisect handler.
BISECT_VALID_COLLECTIONS = [
BOOT_COLLECTION,
diff --git a/app/models/test_case.py b/app/models/test_case.py
new file mode 100644
index 0000000..1d95233
--- /dev/null
+++ b/app/models/test_case.py
@@ -0,0 +1,276 @@
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""The model that represent a test case document in the database."""
+
+import copy
+import types
+
+import models
+import models.base as mbase
+
+
+# pylint: disable=invalid-name
+# pylint: disable=too-many-instance-attributes
+class TestCaseDocument(mbase.BaseDocument):
+ """Model for a test case document.
+
+ A test case is the smallest unit if a test set: it is the actual test that
+ is run and that reports a result.
+ """
+
+ def __init__(self, name, test_suite_id, version):
+ """
+
+ :param name: The name given to this test case.
+ :type name: string
+ :param test_suite_id: The ID of the test suite this test case
+ belongs to.
+ :type test_suite_id: string
+ :param version: The version of the JSON schema of this test case.
+ :type version: string
+ """
+ self._created_on = None
+ self._id = None
+ self._name = name
+ self._version = version
+
+ self._test_suite_id = test_suite_id
+
+ self._attachments = []
+ self._measurements = []
+ self._parameters = {}
+ self._status = "PASS"
+
+ self.definition_uri = None
+ self.kvm_guest = None
+ self.maximum = None
+ self.metadata = {}
+ self.minimum = None
+ self.samples = None
+ self.samples_sqr_sum = None
+ self.samples_sum = None
+ self.test_set_id = None
+ self.time = -1
+ self.vcs_commit = None
+
+ @property
+ def collection(self):
+ return models.TEST_CASE_COLLECTION
+
+ @property
+ def name(self):
+ """The name of the test set."""
+ return self._name
+
+ @property
+ def id(self):
+ """The ID of the test set as registered in the database."""
+ return self._id
+
+ @id.setter
+ def id(self, value):
+ """Set the test set ID."""
+ self._id = value
+
+ @property
+ def version(self):
+ """The schema version of this test set."""
+ return self._version
+
+ @version.setter
+ def version(self, value):
+ """Set the schema version of this test set."""
+ self._version = value
+
+ @property
+ def created_on(self):
+ """The creation date of this test set."""
+ return self._created_on
+
+ @created_on.setter
+ def created_on(self, value):
+ """Set the creation date of this test set."""
+ self._created_on = value
+
+ @property
+ def parameters(self):
+ """The parameters that this test set ran with."""
+ return self._parameters
+
+ @parameters.setter
+ def parameters(self, value):
+ """Set the parameters this test set ran with.
+
+ :param value: The parameters data structure.
+ :type value: dict
+ """
+ if not value:
+ value = {}
+ if not isinstance(value, types.DictionaryType):
+ raise ValueError(
+ "The parameters need to be passed as a dictionary")
+ self._parameters = value
+
+ @property
+ def test_suite_id(self):
+ """The ID of the associated test suite."""
+ return self._test_suite_id
+
+ @test_suite_id.setter
+ def test_suite_id(self, value):
+ """Set the associated test suite ID.
+
+ :param value: The test suite ID.
+ :type value: string
+ """
+ self._test_suite_id = value
+
+ @property
+ def attachments(self):
+ """The attachments associated with this test case."""
+ return self._attachments
+
+ @attachments.setter
+ def attachments(self, value):
+ """Set the attachments associated with this test case.
+
+ :param value: The attachments of this test case.
+ :type value: list
+ """
+ if not value:
+ value = []
+ if not isinstance(value, types.ListType):
+ raise ValueError("Attachments need to be passed as a list")
+ self._attachments = value
+
+ def add_attachment(self, attachment):
+ """Add (and append) an attachment to this test case.
+
+ An attachment should be a non-empty dictionary like data structure.
+ Empty values will not be stored in this data structure.
+
+ :param attachment: The attachment to add.
+ :type attachment: dict
+ :raise ValueError if the passed value is not a dict.
+ """
+ if all([attachment, isinstance(attachment, types.DictType)]):
+ self._attachments.append(attachment)
+ else:
+ raise ValueError(
+ "Attachment should be non-empty dictionary-like object")
+
+ @property
+ def measurements(self):
+ """The measurements registered by this test case."""
+ return self._measurements
+
+ @measurements.setter
+ def measurements(self, value):
+ """Set the measurements registered by this test case.
+
+ :param value: The registered measurements.
+ :type value: list
+ """
+ if not value:
+ value = []
+ if not isinstance(value, types.ListType):
+ raise ValueError("Measurements need to be passed as a list")
+ self._measurements = value
+
+ def add_measurement(self, measurement):
+ """Add a single measurement to this test case.
+
+ A measurement should be a non-empty dictionary-like data structure.
+ Empty values will not be stored in this data structure. To register an
+ empty result it is still necessary to provide a valid measurement data
+ structure.
+
+ :param measurement: The registered measurement.
+ :type measurement: dict
+ """
+ if all([measurement, isinstance(measurement, types.DictionaryType)]):
+ self._measurements.append(measurement)
+ else:
+ raise ValueError(
+ "Measurement should be non-empty dictionary-like object")
+
+ @property
+ def status(self):
+ """The status of this test case."""
+ return self._status
+
+ @status.setter
+ def status(self, value):
+ """Set the status of this test case.
+
+ :param value: The status name.
+ :type value: string
+ """
+ if all([value, value in models.VALID_TEST_CASE_STATUS]):
+ self._status = value
+ else:
+ raise ValueError("Unsupported status value provided")
+
+ def to_dict(self):
+ test_case = {
+ models.ATTACHMENTS_KEY: self.attachments,
+ models.CREATED_KEY: self.created_on,
+ models.DEFINITION_URI_KEY: self.definition_uri,
+ models.KVM_GUEST_KEY: self.kvm_guest,
+ models.MAXIMUM_KEY: self.maximum,
+ models.MEASUREMENTS_KEY: self.measurements,
+ models.METADATA_KEY: self.metadata,
+ models.MINIMUM_KEY: self.minimum,
+ models.NAME_KEY: self.name,
+ models.PARAMETERS_KEY: self.parameters,
+ models.SAMPLES_KEY: self.samples,
+ models.SAMPLES_SQUARE_SUM_KEY: self.samples_sqr_sum,
+ models.SAMPLES_SUM_KEY: self.samples_sum,
+ models.STATUS_KEY: self.status,
+ models.TEST_SET_ID_KEY: self.test_set_id,
+ models.TEST_SUITE_ID_KEY: self.test_suite_id,
+ models.TIME_KEY: self.time,
+ models.VCS_COMMIT_KEY: self.vcs_commit,
+ models.VERSION_KEY: self.version
+ }
+
+ if self.id:
+ test_case[models.ID_KEY] = self.id
+
+ return test_case
+
+ @staticmethod
+ def from_json(json_obj):
+ test_case = None
+ if isinstance(json_obj, types.DictionaryType):
+ local_obj = copy.deepcopy(json_obj)
+ doc_pop = local_obj.pop
+
+ set_id = doc_pop(models.ID_KEY, None)
+
+ try:
+ name = doc_pop(models.NAME_KEY)
+ test_suite_id = doc_pop(models.TEST_SUITE_ID_KEY)
+ version = doc_pop(models.VERSION_KEY)
+
+ test_case = TestCaseDocument(name, test_suite_id, version)
+ test_case.id = set_id
+
+ for key, val in local_obj.iteritems():
+ setattr(test_case, key, val)
+ except KeyError:
+ # Missing mandatory key? Return None.
+ test_case = None
+
+ return test_case
diff --git a/app/models/tests/test_test_case_model.py b/app/models/tests/test_test_case_model.py
new file mode 100644
index 0000000..c8a5edd
--- /dev/null
+++ b/app/models/tests/test_test_case_model.py
@@ -0,0 +1,269 @@
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+import models.base as mbase
+import models.test_case as mtcase
+
+
+class TestTestCaseModel(unittest.TestCase):
+
+ def test_case_doc_valid_instance(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+ self.assertIsInstance(test_case, mbase.BaseDocument)
+ self.assertEqual(test_case.collection, "test_case")
+
+ def test_case_doc_to_dict(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ test_case.id = "id"
+ test_case.attachments = [{"foo": "bar"}]
+ test_case.created_on = "now"
+ test_case.definition_uri = "scheme://authority/path"
+ test_case.kvm_guest = "kvm_guest"
+ test_case.maximum = 1
+ test_case.measurements = [{"foo": 1}]
+ test_case.metadata = {"foo": "bar"}
+ test_case.minimum = -1
+ test_case.parameters = {"param": "value"}
+ test_case.samples = 10
+ test_case.samples_sqr_sum = 1
+ test_case.samples_sum = 1
+ test_case.status = "FAIL"
+ test_case.test_set_id = "test_set_id"
+ test_case.test_suite_id = "another_id"
+ test_case.time = 10
+ test_case.vcs_commit = "commit_sha"
+ test_case.version = "1.1"
+
+ expected = {
+ "_id": "id",
+ "attachments": [{"foo": "bar"}],
+ "created_on": "now",
+ "definition_uri": "scheme://authority/path",
+ "kvm_guest": "kvm_guest",
+ "maximum": 1,
+ "measurements": [{"foo": 1}],
+ "metadata": {"foo": "bar"},
+ "minimum": -1,
+ "name": "name",
+ "parameters": {"param": "value"},
+ "samples": 10,
+ "samples_sqr_sum": 1,
+ "samples_sum": 1,
+ "status": "FAIL",
+ "test_set_id": "test_set_id",
+ "test_suite_id": "another_id",
+ "time": 10,
+ "vcs_commit": "commit_sha",
+ "version": "1.1"
+ }
+
+ self.assertDictEqual(expected, test_case.to_dict())
+
+ def test_case_doc_to_dict_no_id(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ test_case.attachments = [{"foo": "bar"}]
+ test_case.created_on = "now"
+ test_case.definition_uri = "scheme://authority/path"
+ test_case.kvm_guest = "kvm_guest"
+ test_case.maximum = 1
+ test_case.measurements = [{"foo": 1}]
+ test_case.metadata = {"foo": "bar"}
+ test_case.minimum = -1
+ test_case.parameters = {"param": "value"}
+ test_case.samples = 10
+ test_case.samples_sqr_sum = 1
+ test_case.samples_sum = 1
+ test_case.status = "FAIL"
+ test_case.test_set_id = "test_set_id"
+ test_case.test_suite_id = "another_id"
+ test_case.time = 10
+ test_case.vcs_commit = "commit_sha"
+ test_case.version = "1.1"
+
+ expected = {
+ "attachments": [{"foo": "bar"}],
+ "created_on": "now",
+ "definition_uri": "scheme://authority/path",
+ "kvm_guest": "kvm_guest",
+ "maximum": 1,
+ "measurements": [{"foo": 1}],
+ "metadata": {"foo": "bar"},
+ "minimum": -1,
+ "name": "name",
+ "parameters": {"param": "value"},
+ "samples": 10,
+ "samples_sqr_sum": 1,
+ "samples_sum": 1,
+ "status": "FAIL",
+ "test_set_id": "test_set_id",
+ "test_suite_id": "another_id",
+ "time": 10,
+ "vcs_commit": "commit_sha",
+ "version": "1.1"
+ }
+
+ self.assertDictEqual(expected, test_case.to_dict())
+
+ def test_case_doc_from_json_missing_key(self):
+ test_case = {
+ "_id": "id",
+ "version": "1.0",
+ "test_suite_id": "test_suite_id"
+ }
+
+ self.assertIsNone(mtcase.TestCaseDocument.from_json(test_case))
+
+ def test_case_doc_from_json_wrong_type(self):
+ self.assertIsNone(mtcase.TestCaseDocument.from_json([]))
+ self.assertIsNone(mtcase.TestCaseDocument.from_json(()))
+ self.assertIsNone(mtcase.TestCaseDocument.from_json(""))
+
+ def test_case_doc_from_json(self):
+ case_json = {
+ "_id": "id",
+ "attachments": [{"foo": "bar"}],
+ "created_on": "now",
+ "definition_uri": "scheme://authority/path",
+ "kvm_guest": "kvm_guest",
+ "maximum": 1,
+ "measurements": [{"foo": 1}],
+ "metadata": {"foo": "bar"},
+ "minimum": -1,
+ "name": "name",
+ "parameters": {"param": "value"},
+ "samples": 10,
+ "samples_sqr_sum": 1,
+ "samples_sum": 1,
+ "status": "FAIL",
+ "test_set_id": "test_set_id",
+ "test_suite_id": "another_id",
+ "time": 10,
+ "vcs_commit": "commit_sha",
+ "version": "1.1"
+ }
+
+ test_case = mtcase.TestCaseDocument.from_json(case_json)
+
+ self.assertIsInstance(test_case, mtcase.TestCaseDocument)
+ self.assertDictEqual(case_json, test_case.to_dict())
+
+ def test_case_doc_parameters_setter(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def parameters_setter(value):
+ test_case.parameters = value
+
+ self.assertRaises(ValueError, parameters_setter, ["foo"])
+ self.assertRaises(ValueError, parameters_setter, ("foo"))
+ self.assertRaises(ValueError, parameters_setter, "foo")
+
+ parameters_setter([])
+ self.assertDictEqual({}, test_case.parameters)
+ parameters_setter(())
+ self.assertDictEqual({}, test_case.parameters)
+ parameters_setter(None)
+ self.assertDictEqual({}, test_case.parameters)
+ parameters_setter("")
+ self.assertDictEqual({}, test_case.parameters)
+
+ def test_case_doc_attachments_setter(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def attachments_setter(value):
+ test_case.attachments = value
+
+ self.assertRaises(ValueError, attachments_setter, {"foo": "bar"})
+ self.assertRaises(ValueError, attachments_setter, "foo")
+
+ attachments_setter([])
+ self.assertListEqual([], test_case.attachments)
+ attachments_setter(())
+ self.assertListEqual([], test_case.attachments)
+ attachments_setter(None)
+ self.assertListEqual([], test_case.attachments)
+ attachments_setter("")
+ self.assertListEqual([], test_case.attachments)
+ attachments_setter({})
+ self.assertListEqual([], test_case.attachments)
+
+ def test_case_doc_add_attachments(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def add_attachment(value):
+ test_case.add_attachment(value)
+
+ test_case.attachments = [{"foo": "bar"}]
+ test_case.add_attachment({"baz": "foo"})
+
+ expected = [{"foo": "bar"}, {"baz": "foo"}]
+ self.assertListEqual(expected, test_case.attachments)
+
+ self.assertRaises(ValueError, add_attachment, "")
+ self.assertRaises(ValueError, add_attachment, [])
+ self.assertRaises(ValueError, add_attachment, {})
+ self.assertRaises(ValueError, add_attachment, ())
+
+ def test_case_doc_measurements_setter(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def measurements_setter(value):
+ test_case.measurements = value
+
+ self.assertRaises(ValueError, measurements_setter, {"foo": "bar"})
+ self.assertRaises(ValueError, measurements_setter, "foo")
+
+ measurements_setter([])
+ self.assertListEqual([], test_case.measurements)
+ measurements_setter(())
+ self.assertListEqual([], test_case.measurements)
+ measurements_setter(None)
+ self.assertListEqual([], test_case.measurements)
+ measurements_setter("")
+ self.assertListEqual([], test_case.measurements)
+ measurements_setter({})
+ self.assertListEqual([], test_case.measurements)
+
+ def test_case_doc_add_measurement(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def add_measurement(value):
+ test_case.add_measurement(value)
+
+ test_case.measurements = [{"foo": "bar"}]
+ test_case.add_measurement({"baz": "foo"})
+
+ expected = [{"foo": "bar"}, {"baz": "foo"}]
+ self.assertListEqual(expected, test_case.measurements)
+
+ self.assertRaises(ValueError, add_measurement, "")
+ self.assertRaises(ValueError, add_measurement, [])
+ self.assertRaises(ValueError, add_measurement, {})
+ self.assertRaises(ValueError, add_measurement, ())
+
+ def test_case_doc_set_status(self):
+ test_case = mtcase.TestCaseDocument("name", "test_suite_id", "1.0")
+
+ def set_status(value):
+ test_case.status = value
+
+ self.assertEqual("PASS", test_case.status)
+ self.assertRaises(ValueError, set_status, "FOO")
+ self.assertRaises(ValueError, set_status, "")
+ self.assertRaises(ValueError, set_status, 1)
+ self.assertRaises(ValueError, set_status, {})
+ self.assertRaises(ValueError, set_status, [])
+ self.assertRaises(ValueError, set_status, ())
diff --git a/app/tests/__init__.py b/app/tests/__init__.py
index 6a08ada..6a9245b 100644
--- a/app/tests/__init__.py
+++ b/app/tests/__init__.py
@@ -41,8 +41,9 @@ def test_modules():
"models.tests.test_job_model",
"models.tests.test_lab_model",
"models.tests.test_report_model",
- "models.tests.test_test_suite_model",
+ "models.tests.test_test_case_model",
"models.tests.test_test_set_model",
+ "models.tests.test_test_suite_model",
"models.tests.test_token_model",
"utils.batch.tests.test_batch_common",
"utils.bisect.tests.test_bisect",