Skip to content
Snippets Groups Projects
Select Git revision
  • 6708e2b9e8fe12434fe86cef312e53fe6ab1add5
  • main default protected
  • dev
  • f-spss-value-label-name
  • f-unmod
  • f-checkidentical
  • f-simple-breakpoint
  • f-new-debug-tree
  • f-existing-file-id
  • f-no-ident
  • f-collect-problems
  • f-refactor-debug-tree
  • v0.13.0
  • v0.12.0
  • v0.11.0
  • v0.10.1
  • v0.10.0
  • v0.9.1
  • v0.9.0
  • v0.8.0
  • v0.7.1
  • v0.7.0
  • v0.6.0
  • v0.5.0
  • v0.4.0
  • v0.3.0
  • v0.2.0
  • v0.1.0
28 results

test_macros.py

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    test_yaml_model_parser.py 12.02 KiB
    import unittest
    from datetime import date
    from tempfile import NamedTemporaryFile
    from pytest import raises
    
    import caosdb as db
    from caosadvancedtools.models.parser import (TwiceDefinedException,
                                                 YamlDefinitionError,
                                                 parse_model_from_string,
                                                 parse_model_from_yaml)
    
    
    def to_file(string):
        f = NamedTemporaryFile(mode="w", delete=False)
        f.write(string)
        f.close()
    
        return f.name
    
    # TODO: check purpose of this function... add documentation
    
    
    def parse_str(string):
        parse_model_from_yaml(to_file(string))
    
    
    def has_property(el, name):
        for p in el.get_properties():
            if p.name == name:
                return True
    
        return False
    
    
    def has_parent(el, name):
        for p in el.get_parents():
            if p.name == name:
                return True
    
        return False
    
    
    class TwiceTest(unittest.TestCase):
        def test_defined_once(self):
            string = """
    RT1:
      recommended_properties:
        a:
    RT2:
      recommended_properties:
        RT1:
    RT3:
      recommended_properties:
        RT4:
          recommended_properties:
            a:
    RT4:
    """
            model = parse_model_from_yaml(to_file(string))
            assert has_property(model["RT1"], "a")
            assert has_property(model["RT4"], "a")
    
        def test_defined_twice(self):
            string = """
    RT1:
      recommended_properties:
        a:
    RT2:
      recommended_properties:
        RT1:
          recommended_properties:
            a:
    """
    
            self.assertRaises(TwiceDefinedException,
                              lambda: parse_model_from_yaml(to_file(string)))
    
        def test_typical_case(self):
            string = """
    RT1:
      recommended_properties:
        p1:
          datatype: TEXT
          description: shiet egal
      obligatory_properties:
        p2:
          datatype: TEXT
    RT2:
      description: "This is awesome"
      inherit_from_suggested:
      - RT1
      - RT4
      obligatory_properties:
        RT1:
        p3:
          datatype: DATETIME
      recommended_properties:
        p4:
        RT4:
    p1:
    p5:
    RT5:
      """
            parse_model_from_yaml(to_file(string))
    
        def test_wrong_kind(self):
            string = """
    - RT1:
    - RT2:
    """
            self.assertRaises(
                ValueError, lambda: parse_model_from_yaml(to_file(string)))
    
        def test_unknown_kwarg(self):
            string = """
    RT1:
      datetime:
        p1:
    """
            self.assertRaises(
                ValueError, lambda: parse_model_from_yaml(to_file(string)))
    
        def test_definition_in_inheritance(self):
            string = """
    RT2:
      description: "This is awesome"
      inherit_from_suggested:
      - RT1:
        description: "tach"
    """
            self.assertRaises(
                ValueError, lambda: parse_model_from_yaml(to_file(string)))
    
        def test_inheritance(self):
            string = """
    RT1:
      description: "This is awesome"
      inherit_from_suggested:
      - RT2
      inherit_from_recommended:
      - RT3
      inherit_from_obligatory:
      - RT4
      - RT5
    RT2:
    RT3:
    RT4:
    RT5:
    """
            model = parse_model_from_yaml(to_file(string))
            assert has_parent(model["RT1"], "RT2")
            assert (model["RT1"].get_parent(
                "RT2")._flags["inheritance"] == db.SUGGESTED)
            assert has_parent(model["RT1"], "RT3")
            assert (model["RT1"].get_parent(
                "RT3")._flags["inheritance"] == db.RECOMMENDED)
            assert has_parent(model["RT1"], "RT4")
            assert (model["RT1"].get_parent(
                "RT4")._flags["inheritance"] == db.OBLIGATORY)
            assert has_parent(model["RT1"], "RT5")
            assert (model["RT1"].get_parent(
                "RT5")._flags["inheritance"] == db.OBLIGATORY)
    
        def test_properties(self):
            string = """
    RT1:
      description: "This is awesome"
      recommended_properties:
        RT2:
      suggested_properties:
        RT3:
      obligatory_properties:
        RT4:
          recommended_properties:
            RT2:
        RT5:
    """
            model = parse_model_from_yaml(to_file(string))
            assert has_property(model["RT1"], "RT2")
            assert model["RT1"].get_importance("RT2") == db.RECOMMENDED
            assert has_property(model["RT1"], "RT3")
            assert model["RT1"].get_importance("RT3") == db.SUGGESTED
            assert has_property(model["RT1"], "RT4")
            assert model["RT1"].get_importance("RT4") == db.OBLIGATORY
            assert has_property(model["RT1"], "RT5")
            assert model["RT1"].get_importance("RT5") == db.OBLIGATORY
            assert has_property(model["RT4"], "RT2")
            assert model["RT4"].get_importance("RT2") == db.RECOMMENDED
    
        def test_datatype(self):
            string = """
    p1:
      datatype: TEXT
    """
            parse_model_from_yaml(to_file(string))
            string = """
    p2:
      datatype: TXT
    """
            self.assertRaises(ValueError, parse_model_from_yaml, to_file(string))
    
    
    class ListTest(unittest.TestCase):
        def test_list(self):
            string = """
    RT1:
      recommended_properties:
        a:
          datatype: LIST(RT2)
        b:
          datatype: LIST(TEXT)
        c:
          datatype: LIST<TEXT>
    RT2:
    """
            model = parse_model_from_yaml(to_file(string))
    
            self.assertTrue(isinstance(model['b'], db.Property))
            self.assertEqual(model['b'].datatype, db.LIST(db.TEXT))
            self.assertTrue(isinstance(model['c'], db.Property))
            self.assertEqual(model['c'].datatype, db.LIST(db.TEXT))
    
            # This failed for an older version of caosdb-models
            string_list = """
    A:
      obligatory_properties:
        B:
          datatype: LIST(B)
    B:
      obligatory_properties:
        c:
          datatype: INTEGER
    """
            model = parse_model_from_yaml(to_file(string_list))
            self.assertTrue(isinstance(model['A'], db.RecordType))
            self.assertEqual(model['A'].properties[0].datatype, db.LIST("B"))
    
    
    class ParserTest(unittest.TestCase):
        """Generic tests for good and bad syntax."""
    
        def test_empty_property_list(self):
            """Emtpy property lists are allowed now."""
            empty = """
    A:
      obligatory_properties:
    """
            parse_str(empty)
    
        def test_non_string_name(self):
            """Test for when the name does not look like a string to YAML."""
            name_int = """1:
      recommended_properties:
        1.2:
        Null:
        0x0:
        010:
    """
            model = parse_model_from_string(name_int)
            self.assertEqual(len(model), 5)
            for key in model.keys():
                self.assertIsInstance(key, str)
    
        def test_unexpected_keyword(self):
            """Test for when keywords happen at places where they should not be."""
            yaml = """A:
      obligatory_properties:
        recommended_properties:
    """
            with self.assertRaises(YamlDefinitionError) as yde:
                parse_model_from_string(yaml)
            self.assertIn("line 3", yde.exception.args[0])
            self.assertIn("recommended_properties", yde.exception.args[0])
    
        def test_parents_list(self):
            """Parents must be a list."""
            yaml = """A:
      inherit_from_obligatory:
        A:
    """
            with self.assertRaises(YamlDefinitionError) as yde:
                parse_model_from_string(yaml)
            self.assertIn("line 3", yde.exception.args[0])
    
        def test_reference_property(self):
            """Test correct creation of reference property using an RT."""
            modeldef = """A:
      recommended_properties:
        ref:
          datatype: LIST<A>
    """
            model = parse_model_from_string(modeldef)
            self.assertEqual(len(model), 2)
            for key in model.keys():
                if key == "A":
                    self.assertTrue(isinstance(model[key], db.RecordType))
                elif key == "ref":
                    self.assertTrue(isinstance(model[key], db.Property))
                    self.assertEqual(model[key].datatype, "LIST<A>")
    
    
    class ExternTest(unittest.TestCase):
        """TODO Testing the "extern" keyword in the YAML."""
        @unittest.expectedFailure
        def test_extern(self):
            raise NotImplementedError("Extern testing is not implemented yet.")
    
    
    class ErrorMessageTest(unittest.TestCase):
        """Tests for understandable error messages."""
    
        # Note: This was changed with implementation of role keyword
        @unittest.expectedFailure
        def test_non_dict(self):
            """When a value is given, where a list or mapping is expected."""
            recordtype_value = """
    A: "some class"
    """
            recommended_value = """
    A:
      recommended_properties: 23
    """
            property_value = """
    prop:
      datatype: DOUBLE
    A:
      recommended_properties:
      - prop: 3.14
    """
            # Failing strings and the lines where they fail
            failing = {
                recordtype_value: 2,
                recommended_value: 3,
                property_value: 6
            }
            for string, line in failing.items():
                # parse_str(string)
                with self.assertRaises(YamlDefinitionError) as yde:
                    parse_str(string)
                assert("line {}".format(line) in yde.exception.args[0])
    
    
    def test_define_role():
        model = """
    A:
      role: Record
    """
        entities = parse_model_from_string(model)
        assert "A" in entities
        assert isinstance(entities["A"], db.Record)
        assert entities["A"].role == "Record"
    
        model = """
    A:
      role: Record
      inherit_from_obligatory:
      - C
      obligatory_properties:
        b:
    b:
      datatype: INTEGER
    C:
      obligatory_properties:
        b:
    D:
      role: RecordType
    """
        entities = parse_model_from_string(model)
        for l, ent in (("A", "Record"), ("b", "Property"),
                       ("C", "RecordType"), ("D", "RecordType")):
            assert l in entities
            assert isinstance(entities[l], getattr(db, ent))
            assert entities[l].role == ent
    
        assert entities["A"].parents[0].name == "C"
        assert entities["A"].name == "A"
    
        assert entities["A"].properties[0].name == "b"
        assert entities["A"].properties[0].value is None
    
        assert entities["C"].properties[0].name == "b"
        assert entities["C"].properties[0].value is None
    
        model = """
    A:
      role: Record
      obligatory_properties:
        b: 42
    b:
      datatype: INTEGER
    """
    
        entities = parse_model_from_string(model)
        assert entities["A"].get_property("b").value == 42
        assert entities["b"].value is None
    
        model = """
    b:
      datatype: INTEGER
      value: 18
    """
        entities = parse_model_from_string(model)
        assert entities["b"].value == 18
    
    
    def test_issue_72():
        """Tests for
        https://gitlab.indiscale.com/caosdb/src/caosdb-advanced-user-tools/-/issues/72
    
        In some cases, faulty values would be read in for properties without a
        specified value.
    
        """
        model = """
    Experiment:
      obligatory_properties:
        date:
          datatype: DATETIME
          description: 'date of the experiment'
        identifier:
          datatype: TEXT
          description: 'identifier of the experiment'
        temperature:
          datatype: DOUBLE
          description: 'temp'
    TestExperiment:
      role: Record
      inherit_from_obligatory:
        - Experiment
      obligatory_properties:
        date: 2022-03-02
        identifier: Test
        temperature: 23
      recommended_properties:
        additional_prop:
          datatype: INTEGER
          value: 7
    """
        entities = parse_model_from_string(model)
        assert "Experiment" in entities
        assert "date" in entities
        assert "identifier" in entities
        assert "temperature" in entities
        assert "TestExperiment" in entities
        assert "additional_prop" in entities
        assert isinstance(entities["Experiment"], db.RecordType)
    
        assert entities["Experiment"].get_property("date") is not None
        # No value is set, so this has to be None
        assert entities["Experiment"].get_property("date").value is None
    
        assert entities["Experiment"].get_property("identifier") is not None
        assert entities["Experiment"].get_property("identifier").value is None
    
        assert entities["Experiment"].get_property("temperature") is not None
        assert entities["Experiment"].get_property("temperature").value is None
    
        test_rec = entities["TestExperiment"]
        assert isinstance(test_rec, db.Record)
        assert test_rec.get_property("date").value == date(2022, 3, 2)
        assert test_rec.get_property("identifier").value == "Test"
        assert test_rec.get_property("temperature").value == 23
        assert test_rec.get_property("additional_prop").value == 7
    
    
    def test_file_role():
        """Not implemented for now, see
        https://gitlab.indiscale.com/caosdb/src/caosdb-advanced-user-tools/-/issues/74.
    
        """
        model = """
    F:
      role: File
    """
        with raises(NotImplementedError):
            entities = parse_model_from_string(model)