def test_warning_when_prediction_method_and_no_probabilities(): """ Test compute_eval_from_predictions logs a warning if a prediction method is provided but the predictions file doesn't contain probabilities. """ lc = LogCapture() lc.begin() pred_path = join(_my_dir, 'other', 'test_compute_eval_from_predictions_predictions.tsv') input_path = join(_my_dir, 'other', 'test_compute_eval_from_predictions.jsonlines') # we need to capture stdout since that's what main() writes to compute_eval_from_predictions_cmd = [input_path, pred_path, 'pearson', 'unweighted_kappa', '--method', 'highest'] try: old_stdout = sys.stdout old_stderr = sys.stderr sys.stdout = mystdout = StringIO() sys.stderr = mystderr = StringIO() cefp.main(compute_eval_from_predictions_cmd) score_rows = mystdout.getvalue().strip().split('\n') err = mystderr.getvalue() print(err) finally: sys.stdout = old_stdout sys.stderr = old_stderr log_msg = ("skll.utilities.compute_eval_from_predictions: WARNING: A prediction " "method was provided, but the predictions file doesn't contain " "probabilities. Ignoring prediction method 'highest'.") eq_(lc.handler.buffer[-1], log_msg)
def test_get_logger(self): lc = LogCapture() lc.begin() logger = log.get_logger() logger.debug('test-debug') logger.info('test-info') lc.end() self.assertEquals("giftwrap: INFO: test-info", lc.handler.buffer[0])
def test_get_logger(self): lc = LogCapture() lc.begin() logger = logging.getLogger('giftwrap') logger.setLevel(logging.INFO) logger.debug('test-debug') logger.info('test-info') lc.end() self.assertEqual("giftwrap: INFO: test-info", lc.handler.buffer[0])
def test_get_logger(self): lc = LogCapture() lc.begin() logger = logging.getLogger("giftwrap") logger.setLevel(logging.INFO) logger.debug("test-debug") logger.info("test-info") lc.end() self.assertEqual("giftwrap: INFO: test-info", lc.handler.buffer[0])
def test_get_logger_debug(self): lc = LogCapture() lc.begin() logger = logging.getLogger('giftwrap') logger.setLevel(logging.DEBUG) logger.info('test-info') logger.debug('test-debug') lc.end() self.assertEquals("giftwrap: INFO: test-info", lc.handler.buffer[0]) self.assertEquals("giftwrap: DEBUG: test-debug", lc.handler.buffer[1])
class TestNormaLoader(TestCase): """ Unit tests for the NormaLoader class. """ def setUp(self): self.data_dir = TestDataLocator.get_data_dir() + os.sep self.maxDiff = None # Log capturing self.log = LogCapture() self.log.logformat = '%(levelname)s: %(message)s' self.log.begin() def test_bad_filename_extension(self): """ Confirm that exception is raised when input file has .txt extension rather than .orm extension. """ with self.assertRaises(Exception) as ex: NormaLoader("test.txt") self.assertEqual(ex.exception.message, "Input filename must have .orm extension.") def test_bad_root_element(self): """ Confirm that exception is raised when the root element of the XML is not <ormRoot:ORM2>. """ with self.assertRaises(Exception) as ex: NormaLoader(self.data_dir + "bad_root_element.orm") self.assertEqual(ex.exception.message, "Root of input file must be <ormRoot:ORM2>.") def test_no_model_element(self): """ Confirm that exception is raised when the XML does not contain an ORMModel element. """ with self.assertRaises(Exception) as ex: NormaLoader(self.data_dir + "no_model_element.orm") self.assertEqual(ex.exception.message, "Cannot find <orm:ORMModel> in input file.") def test_empty_model(self): """ Confirm a successful parse on a small input file. """ model = NormaLoader(self.data_dir + "empty_model.orm").model self.assertEqual(model.object_types.count(), 0) self.assertEqual(model.fact_types.count(), 0) self.assertEqual(model.constraints.count(), 0) def test_adding_invalid_element(self): """ Confirm exception is raised if an invalid object is passed to the _add() method.""" loader = NormaLoader(self.data_dir + "empty_model.orm") with self.assertRaises(Exception) as ex: loader._add(ModelElement(uid="123", name="ABC")) self.assertEqual(ex.exception.message, "Unexpected model element type") def test_subtypes(self): """ Confirm that subtype derivation rule omission note is added to self.omissions and that subtype constraints load properly. Also confirm that if inclusive-or or XOR constraints are present on a subtype, they are reported in the omissions. """ loader = NormaLoader(self.data_dir + "subtype_with_derivation.orm") model = loader.model self.assertItemsEqual(loader.omissions, [ "Subtype derivation rule for B", "Subtype exclusion constraint ExclusiveOrConstraint1", "Subtype inclusive-or constraint InclusiveOrConstraint1", "Subtype inclusive-or constraint InclusiveOrConstraint2" ]) cons1 = model.constraints.get("BIsASubtypeOfA") self.assertIs(cons1.subtype, model.object_types.get("B")) self.assertIs(cons1.supertype, model.object_types.get("A")) self.assertEquals(cons1.covers, [cons1.subtype, cons1.supertype]) self.assertTrue(cons1.idpath) self.assertFalse(model.constraints.get("BIsASubtypeOfC").idpath) def test_subtypefact_constraints(self): """ Confirm IUC and mandatory constraints on implicit subtype fact types are ignored, because those fact types are ignored. """ loader = NormaLoader(self.data_dir + "subtype_with_derivation.orm") model = loader.model # Note: the mandatory and uniqueness constraints below are on the # reference fact types (e.g. ZHasZId) expected = [ 'CIsASubtypeOfZ', 'AIsASubtypeOfZ', 'BIsASubtypeOfC', 'BIsASubtypeOfA', 'SimpleMandatoryConstraint7', 'InternalUniquenessConstraint13', 'InternalUniquenessConstraint14', 'SimpleMandatoryConstraint1', 'InternalUniquenessConstraint1', 'InternalUniquenessConstraint2', 'SimpleMandatoryConstraint3', 'InternalUniquenessConstraint5', 'InternalUniquenessConstraint6' ] actual = [cons.name for cons in model.constraints] self.assertItemsEqual(actual, expected) def test_subtype_exception(self): """ Confirm subtype exception fires for corrupted data. """ with self.assertRaises(Exception) as ex: loader = NormaLoader(self.data_dir + "corrupted_subtype.orm") self.assertEquals(ex.exception.message, "Cannot load subtype constraint.") def test_load_object_types(self): """ Test of object type load. """ self.log.beforeTest(None) loader = NormaLoader(self.data_dir + "object_type_tests.orm") model = loader.model # No derivation rules self.assertItemsEqual(loader.omissions, []) self.assertItemsEqual(self.log.formatLogRecords(), []) # Overall count self.assertEqual(model.object_types.count(), 10) # Independent Entity this = model.object_types.get("D") self.assertTrue(this.independent) self.assertIs(this.identifying_constraint, model.constraints.get("InternalUniquenessConstraint7")) self.assertIsInstance(this, ObjectType.EntityType) # Non-independent Entity this = model.object_types.get("C") self.assertFalse(this.independent) # Independent value this = model.object_types.get("V2") self.assertTrue(this.independent) self.assertEquals(this.uid, "_FD295AC2-D845-48E4-9E09-2E48EC99E3F3") self.assertIsInstance(this, ObjectType.ValueType) # Non-independent value this = model.object_types.get("V1") self.assertFalse(this.independent) # Implicit value (created by unary fact type): should not be loaded this = model.object_types.get("A exists") self.assertIsNone(this) # Objectified Independent this = model.object_types.get("CHasV1") self.assertTrue(this.independent) self.assertIsInstance(this, ObjectType.ObjectifiedType) # Objectified Non-independent this = model.object_types.get("V1HasV2") nested = model.fact_types.get("V1HasV2") self.assertFalse(this.independent) self.assertIs(this.identifying_constraint, model.constraints.get("InternalUniquenessConstraint14")) self.assertIs(this.nested_fact_type, nested) # Implicit Objectified (Created by ternary, should not load) this = model.object_types.get("V1HasDHasV2") self.assertIsNone(this) self.log.afterTest(None) def test_played_roles(self): """ Confirm that object types know the list of roles they play. """ model = NormaLoader(self.data_dir + "object_type_tests.orm").model obj_type0 = model.object_types.get("B") obj_type1 = model.object_types.get("V1HasV2") obj_type2 = model.object_types.get("D") self.assertItemsEqual(obj_type0.roles, []) r1 = model.fact_types.get("V1HasV2Exists").roles[0] self.assertItemsEqual(obj_type1.roles, [r1]) r1 = model.fact_types.get("V1HasDHasV2").roles[1] r2 = model.fact_types.get("DHasDId").roles[0] self.assertItemsEqual(obj_type2.roles, [r1, r2]) def test_data_type_load(self): """ Confirm data types are loaded properly for Value Types. """ model = NormaLoader(self.data_dir + "data_types.orm").model ot = model.object_types self.assertIsInstance(ot.get("A").domain, Domain.StringDomain) # data type undefined self.assertIsInstance(ot.get("B").domain, Domain.BoolDomain) # True or false self.assertIsInstance(ot.get("C").domain, Domain.BoolDomain) # Yes or no self.assertIsInstance(ot.get("D").domain, Domain.IntegerDomain) # auto counter self.assertIsInstance(ot.get("E").domain, Domain.FloatDomain) # float self.assertIsInstance(ot.get("Special").domain, Domain.FloatDomain) # float self.assertIsInstance(ot.get("F").domain, Domain.FloatDomain) # money self.assertIsInstance(ot.get("G").domain, Domain.IntegerDomain) # big int self.assertIsInstance(ot.get("H").domain, Domain.DateTimeDomain) # timestamp self.assertIsInstance(ot.get("I").domain, Domain.DateDomain) # date self.assertIsInstance(ot.get("J").domain, Domain.TimeDomain) # time self.assertIsInstance(ot.get("K").domain, Domain.StringDomain) # text # Confirm for A and K that prefix is 'A' and 'K' obj = ot.get("A") actual = obj.domain.draw(2) expect = ['A0', 'A1'] self.assertItemsEqual(actual, expect) obj = ot.get("K") actual = obj.domain.draw(2) expect = ['K0', 'K1'] self.assertItemsEqual(actual, expect) def test_unknown_data_type(self): """ Confirm that domain defaults to StringDomain() if the actual type is unexpected. """ model = NormaLoader(self.data_dir + "unknown_data_types.orm").model obj = model.object_types.get("A") self.assertIsInstance(obj.domain, Domain.StringDomain) self.assertItemsEqual(obj.domain.draw(1), ["A0"]) obj = model.object_types.get("B") self.assertIsInstance(obj.domain, Domain.StringDomain) self.assertItemsEqual(obj.domain.draw(1), ["B0"]) def test_value_constraint_on_types(self): """ Confirm that value constraints on value types are loaded. """ model = NormaLoader(self.data_dir + "test_value_type_value_constraint.orm").model cons1 = model.constraints.get("VC_A") self.assertEquals(cons1.uid, "_9F61B75E-FB59-456F-97A9-E4CF104FABE5") self.assertIs(cons1.covers[0], model.object_types.get("A")) expected = set([1, 2, 6] + range(10, 20) + range(101, 200)) self.assertItemsEqual(cons1.domain.draw(112), expected) self.assertEquals(cons1.size, 112) def test_load_fact_types(self): """ Confirm fact types load successfully. """ loader = NormaLoader(self.data_dir + "fact_type_tests.orm") model = loader.model # Confirm reference scheme fact type loads ftype1 = model.fact_types.get("AHasAId") self.assertEquals(ftype1.uid, "_8C96FD40-5E82-4E8F-B5EE-C6CEE8BCF74B") self.assertIsInstance(ftype1, FactType) # Confirm derivation rule added to omissions self.assertItemsEqual(loader.omissions, [ "Fact type derivation rule for AHasB", "Fact type derivation rule for AHasAId" ]) # Check role player typea = model.object_types.get("A") typeb = model.object_types.get("B") ahasb = model.fact_types.get("AHasB") self.assertIs(ahasb.roles[0].player, typea) self.assertIs(ahasb.roles[1].player, typeb) # Check role fact type self.assertIs(ahasb.roles[0].fact_type, ahasb) self.assertIs(ahasb.roles[1].fact_type, ahasb) # Check that implicit role added to unary is removed aexists = model.fact_types.get("AExists") self.assertIs(aexists.roles[0].player, typea) self.assertEquals(aexists.arity(), 1) # Test role value constraint cons1 = model.constraints.get("RoleValueConstraint1") expected = ['A', 'Dog', '3.567', '12/23/2014'] self.assertItemsEqual(cons1.domain.draw(4), expected) def test_forced_implicit(self): """ Exercise branch in _load_fact_types when arity() = 0. """ loader = NormaLoader(self.data_dir + "forced_implicit_binary.orm") model = loader.model # The object types are both marked implicit so the corresponding # fact type should not load either. self.assertEquals(model.object_types.count(), 0) self.assertEquals(model.fact_types.count(), 0) def test_derivation_source(self): """ Confirm depricated DerivationSource element is ignored. """ self.log.beforeTest(None) loader = NormaLoader(self.data_dir + "derivation_source.orm") expected = ["Role derivation rule within AHasB"] # Check omissions array self.assertItemsEqual(loader.omissions, expected) # Check log contents expected = ["WARNING: 1 model element was ignored while loading derivation_source.orm."] + \ ["INFO: Ignored " + expected[0]] self.assertItemsEqual(self.log.formatLogRecords(), expected) self.log.afterTest(None) def test_constraints_omitted(self): """ Confirm omitted constraints get saved to omissions list.""" self.log.beforeTest(None) loader = NormaLoader(self.data_dir + "omitted_constraints.orm") expected = [ "Exclusion constraint ExclusionConstraint1", "Exclusion constraint ExclusiveOrConstraint1", "Ring constraint RingConstraint1", "Value comparison constraint ValueComparisonConstraint1" ] # Check omissions array self.assertItemsEqual(loader.omissions, expected) # Check log contents expected = ["WARNING: 4 model elements were ignored while loading omitted_constraints.orm."] + \ ["INFO: Ignored " + msg for msg in expected] self.assertItemsEqual(self.log.formatLogRecords(), expected) self.log.afterTest(None) def test_subset_constraint(self): """ Confirm subset constraints load correctly. """ fname = TestDataLocator.path("subset_constraint.orm") loader = NormaLoader(fname, deontic=True) model = loader.model cons1 = model.constraints.get("SubsetConstraint1") cons2 = model.constraints.get("SubsetConstraint2") cons3 = model.constraints.get("SubsetConstraint3") cons4 = model.constraints.get("SubsetConstraint4") obj_a = model.object_types.get("A") obj_b = model.object_types.get("B") ahasb = model.fact_types.get("AHasB") alikesb = model.fact_types.get("ALikesB") aplus = model.fact_types.get("CPlusAPlusB") aand = model.fact_types.get("AAndBAndC") # Check modality self.assertTrue(cons1.alethic) self.assertFalse(cons2.alethic) # Check unary subset self.assertEquals(len(cons1.superset), 1) self.assertEquals(len(cons1.subset), 1) self.assertIs(cons1.subset[0].player, obj_a) self.assertIs(cons1.superset[0].player, obj_a) self.assertIs(cons1.subset[0].fact_type, ahasb) self.assertIs(cons1.superset[0].fact_type, alikesb) self.assertItemsEqual(cons1.covers, cons1.subset + cons1.superset) # Check binary subset self.assertEquals(len(cons4.superset), 2) self.assertEquals(len(cons4.subset), 2) self.assertIs(cons4.subset[0].fact_type, aplus) self.assertIs(cons4.superset[1].fact_type, aand) self.assertIs(cons4.subset[0].player, cons4.superset[0].player) self.assertIs(cons4.subset[1].player, cons4.superset[1].player) self.assertIs(cons4.subset[1].player, obj_b) # Check ternary subset self.assertEquals(len(cons3.superset), 3) self.assertEquals(len(cons3.subset), 3) self.assertIs(cons3.subset[0].fact_type, aplus) self.assertIs(cons3.superset[2].fact_type, aand) self.assertIs(cons3.subset[2].player, cons3.subset[2].player) self.assertItemsEqual(loader.omissions, []) def test_partial_subset(self): """ Confirm exception fires for invalid subset constraints. """ with self.assertRaises(Exception) as ex: loader = NormaLoader(self.data_dir + "partial_subset.orm") self.assertEquals( ex.exception.message, "Subset constraint SubsetConstraint1 does not have exactly two role sequences" ) def test_implicit_role(self): """ Confirm constraint that covers implicit role is removed. """ model = NormaLoader(self.data_dir + "test_implicit_roles.orm").model self.assertIsNone(model.constraints.get("SubsetConstraint1")) def test_deprecated_join(self): """ Confirm exception fires for models containing deprecated join constraint. """ with self.assertRaises(Exception) as ex: loader = NormaLoader(self.data_dir + "deprecated_join.orm") self.assertEquals( ex.exception.message, "Subset constraint SubsetConstraint1 has deprecated join rule.") def test_join_subset_omission(self): """ Confirm that join subset constraints are (for now) omitted. """ loader = NormaLoader(self.data_dir + "join_subset_omission.orm") self.assertItemsEqual(loader.omissions, [ "Subset constraint SubsetConstraint1 because its join path does not have exactly one JoinPath node." ]) cons = loader.model.constraints.get("SubsetConstraint1") self.assertIsNone(cons) def test_uniqueness_constraint(self): """ Confirm uniqueness constraints load properly. """ fname = TestDataLocator.path("uniqueness_constraints.orm") model = NormaLoader(fname, deontic=True).model int_cons1 = model.constraints.get("InternalUniquenessConstraint4") int_cons2 = model.constraints.get("InternalUniquenessConstraint1") int_cons3 = model.constraints.get("InternalUniquenessConstraint9") ext_cons1 = model.constraints.get("ExternalUniquenessConstraint1") ext_cons2 = model.constraints.get("ExternalUniquenessConstraint2") obj_a = model.object_types.get("A") obj_b = model.object_types.get("B") obj_z = model.object_types.get("Z") ahasb = model.fact_types.get("AHasB") alikesb = model.fact_types.get("ALikesB") tern = model.fact_types.get("AAndAHoldsB") # Check uid self.assertEquals(int_cons1.uid, "_22FD96ED-BE72-4859-8DA0-1A0C98381FEF") # Check internal attribute self.assertTrue(int_cons1.internal) self.assertFalse(ext_cons1.internal) # Check modality self.assertTrue(int_cons1.alethic) self.assertFalse(ext_cons2.alethic) # Check Covers sequence self.assertEquals(len(int_cons1.covers), 1) self.assertIs(int_cons1.covers[0].player, obj_a) self.assertIs(int_cons1.covers[0].fact_type, ahasb) self.assertEquals(len(ext_cons2.covers), 3) self.assertIs(ext_cons2.covers[0].player, obj_a) self.assertIs(ext_cons2.covers[1].player, obj_b) self.assertIs(ext_cons2.covers[2].player, obj_b) self.assertIs(ext_cons2.covers[1].fact_type, tern) self.assertIs(ext_cons2.covers[2].fact_type, alikesb) # Confirm implicit constraints excluded self.assertIsNone( model.constraints.get("InternalUniquenessConstraint2")) self.assertEquals(model.constraints.count(), 8) # Test preferred identifier. self.assertIsNone(int_cons2.identifier_for) # implicit object type # Test preferred identifier self.assertIs(int_cons3.identifier_for, obj_z) self.assertIs(obj_z.identifying_constraint, int_cons3) def test_frequency_constraint(self): """ Confirm frequency constraints load properly. """ model = NormaLoader(self.data_dir + "frequency_constraint.orm").model cons1 = model.constraints.get("FrequencyConstraint1") cons2 = model.constraints.get("FrequencyConstraint2") cons3 = model.constraints.get("FrequencyConstraint3") obj_a = model.object_types.get("A") obj_b = model.object_types.get("B") ahasb = model.fact_types.get("AHasB") alikesb = model.fact_types.get("ALikesB") # Check frequencies self.assertEquals(cons1.min_freq, 1) self.assertEquals(cons1.max_freq, 3) self.assertEquals(cons2.min_freq, 2) self.assertEquals(cons2.max_freq, 2) self.assertEquals(cons3.min_freq, 3) self.assertEquals(cons3.max_freq, float('inf')) # Check covered roles self.assertEquals(len(cons1.covers), 1) self.assertIs(cons1.covers[0].player, obj_b) self.assertIs(cons1.covers[0].fact_type, ahasb) self.assertEquals(len(cons2.covers), 2) self.assertEquals(len(cons3.covers), 2) self.assertIs(cons3.covers[0].fact_type, alikesb) self.assertIs(cons3.covers[1].fact_type, ahasb) # Check internal property self.assertTrue(cons1.internal) self.assertTrue(cons2.internal) self.assertFalse(cons3.internal) def test_bad_role_sequence_node(self): """ Confirm exception fires for invalid node in RoleSequence. """ with self.assertRaises(Exception) as ex: loader = NormaLoader(self.data_dir + "bad_role_sequence.orm") self.assertEquals( ex.exception.message, "Uniqueness constraint " + "ExternalUniquenessConstraint1 has unexpected role sequence.") def test_freq_on_unary(self): """ Confirm loader ignores frequency constraint on unary. it ignores the constraint because NORMA moves the constraint to the implicit role of the implicit binarized fact type. """ loader = NormaLoader(self.data_dir + "implicit_freq_constraint.orm") model = loader.model self.assertIsNone(model.constraints.get("FrequencyConstraint2")) # The 1 constraint is the internal UC on the unary (implicit) self.assertEquals(model.constraints.count(), 1) self.assertEquals(model.fact_types.get("AExists").arity(), 1) def test_mandatory(self): """ Confirm mandatory constraints are successfully loaded. """ loader = NormaLoader(self.data_dir + "mandatory_constraint.orm") model = loader.model # Test implied cons1 = model.constraints.get("ImpliedMandatoryConstraint2") self.assertIsNone(cons1) # Implied due to disjunctive mandatory cons2 = model.constraints.get("SimpleMandatoryConstraint4") self.assertIsNone(cons2) # Implied due to objectification # Confirm constraint count self.assertEquals(model.constraints.count(), 9) # Check loaded mandatories cons = model.constraints.of_type(Constraint.MandatoryConstraint) self.assertEquals(len(cons), 3) s1 = model.constraints.get("SimpleMandatoryConstraint1") s2 = model.constraints.get("SimpleMandatoryConstraint2") i1 = model.constraints.get("InclusiveOrConstraint1") fact_type1 = model.fact_types.get("EHasEId") self.assertTrue(s1.simple) self.assertEquals(len(s1.covers), 1) self.assertIs(s1.covers[0], fact_type1.roles[0]) self.assertTrue(fact_type1.roles[0].mandatory) self.assertFalse(fact_type1.roles[1].mandatory) fact_type2 = model.fact_types.get("EHasB") self.assertTrue(s2.simple) self.assertEquals(len(s2.covers), 1) self.assertIs(s2.covers[0], fact_type2.roles[0]) self.assertTrue(fact_type2.roles[0].mandatory) self.assertFalse(fact_type2.roles[1].mandatory) role1 = model.fact_types.get("EHasB").roles[1] role2 = model.fact_types.get("BIsCool").roles[0] self.assertFalse(i1.simple) self.assertEquals(len(i1.covers), 2) self.assertItemsEqual(i1.covers, [role1, role2]) # For inclusive-or, neither covered role should have "mandatory" true self.assertFalse(role1.mandatory) self.assertFalse(role2.mandatory) # Confirm nothing ignored self.assertItemsEqual(loader.omissions, []) def test_role_names(self): """ Confirm role names are generated properly. """ model = NormaLoader(self.data_dir + "role_names.orm").model ahasb = model.fact_types.get("AHasB") self.assertEquals(ahasb.roles[0].name, "A") self.assertEquals(ahasb.roles[1].name, "B") alikesb = model.fact_types.get("ALikesB") self.assertEquals(alikesb.roles[0].name, "A") self.assertEquals(alikesb.roles[1].name, "Likee") tern = model.fact_types.get("AAndALikeB") self.assertEquals(tern.roles[0].name, "R2") self.assertEquals(tern.roles[1].name, "A") self.assertEquals(tern.roles[2].name, "B") def test_invalid_value_constraint(self): """ Test that invalid value constraint is ignored. """ loader = NormaLoader(self.data_dir + "invalid_value_constraint.orm") model = loader.model actual = loader.omissions expected = [ "Value constraint VC1 because value constraints only support integer ranges" ] self.assertItemsEqual(actual, expected) actual = [cons.name for cons in model.constraints] expected = ["VC2"] self.assertItemsEqual(actual, expected) def test_cardinality_constraints(self): """ Test loading of cardinality constraints. """ fname = TestDataLocator.path("test_cardinality_constraint.orm") loader = NormaLoader(fname, deontic=True) model = loader.model cons1 = model.constraints.get("CC1") self.assertTrue(cons1.alethic) self.assertEquals(cons1.ranges[0].lower, 0) self.assertEquals(cons1.ranges[0].upper, 4) self.assertItemsEqual(cons1.covers, [model.object_types.get("A")]) cons2 = model.constraints.get("CC2") self.assertTrue(cons2.alethic) self.assertEquals(cons2.ranges[0].lower, 2) self.assertEquals(cons2.ranges[0].upper, None) self.assertItemsEqual(cons2.covers, [model.object_types.get("B")]) cons3 = model.constraints.get("CC3") role = model.fact_types.get("AExists").roles[0] self.assertTrue(cons3.alethic) self.assertEquals(cons3.ranges[0].lower, 4) self.assertEquals(cons3.ranges[0].upper, 7) self.assertItemsEqual(cons3.covers, [role]) cons4 = model.constraints.get("CC4") role = model.fact_types.get("BHopes").roles[0] self.assertFalse(cons4.alethic) self.assertEquals(cons4.ranges[0].lower, 4) self.assertEquals(cons4.ranges[0].upper, 4) self.assertItemsEqual(cons4.covers, [role]) cons5 = model.constraints.get("CC5") role = model.fact_types.get("BDances").roles[0] self.assertTrue(cons5.alethic) self.assertEquals(len(cons5.ranges), 4) self.assertEquals(cons5.ranges[0].lower, 0) self.assertEquals(cons5.ranges[0].upper, 2) self.assertEquals(cons5.ranges[1].lower, 5) self.assertEquals(cons5.ranges[1].upper, 5) self.assertEquals(cons5.ranges[2].lower, 8) self.assertEquals(cons5.ranges[2].upper, 10) self.assertEquals(cons5.ranges[3].lower, 12) self.assertEquals(cons5.ranges[3].upper, None) self.assertItemsEqual(cons5.covers, [role]) def test_bad_cardinality_constraint_1(self): """ Test loading of file with two cardinality constraints in one node. """ with self.assertRaises(ValueError) as ex: NormaLoader(self.data_dir + "bad_cardinality_constraint_1.orm") self.assertEquals(ex.exception.message, "Unexpected cardinality constraint format") def test_bad_cardinality_constraint_2(self): """ Test loading of file with badly named cardinality constraint node. """ loader = NormaLoader(self.data_dir + "bad_cardinality_constraint_2.orm") self.assertItemsEqual(loader.unexpected, ["BadCardinalityConstraint"]) def test_bad_cardinality_constraint_3(self): """ Test loading of file with badly named ranges node. """ loader = NormaLoader(self.data_dir + "bad_cardinality_constraint_3.orm") model = loader.model cons = model.constraints.get("C1") self.assertEquals(cons.ranges, []) def test_bad_value_constraint(self): """ Test loading of file with badly named value constraint node. """ loader = NormaLoader(self.data_dir + "bad_value_constraint.orm") self.assertItemsEqual(loader.unexpected, ["BadValueConstraint"]) def test_card_and_value_constraint_on_implicit_type(self): """ Confirm that cardinality and value constraints on implicit types are ignored. This model contains one of each on an implicit boolean object type. """ loader = NormaLoader(self.data_dir + "constraint_on_implicit_type.orm") model = loader.model self.assertEquals(model.constraints.count(), 1) self.assertIsNotNone(model.constraints.get("IUC1")) def test_unexpected_constraint_node(self): """ Confirm the loader catches an unexpected constraint node. """ self.log.beforeTest(None) loader = NormaLoader(self.data_dir + "unexpected_constraint_node.orm") self.assertItemsEqual(loader.unexpected, ["NewConstraint"]) # Check log contents expected = ["WARNING: 1 XML node was unexpected while loading unexpected_constraint_node.orm."] + \ ["INFO: Unexpected NewConstraint"] self.assertItemsEqual(self.log.formatLogRecords(), expected) self.log.afterTest(None) def test_constraint_covers_both_implied_and_explicit_role(self): """ Test a constraint that covers both an implied and an explicit role, which is a VERY UNLIKELY scenario. """ loader = NormaLoader(self.data_dir + \ "constraint_covers_both_implied_and_regular_roles.orm.orm") model = loader.model expected = [ "Uniqueness constraint EUC1 because it covers implied and explicit roles" ] self.assertIsNone(model.constraints.get("EUC1")) self.assertEquals(model.constraints.count(), 2) self.assertItemsEqual(loader.omissions, expected) def test_deontic_constraints(self): """ Test that deontic constraints are correctly ignored. """ fname = TestDataLocator.path("deontic_constraints.orm") loader = NormaLoader(fname, deontic=False) model = loader.model self.assertEquals(model.constraints.count(), 2) self.assertIsNotNone(model.constraints.get("IUC1")) self.assertIsNotNone(model.constraints.get("IUC_unary")) self.assertEquals(loader.omissions, []) loader = NormaLoader(fname, deontic=True) model = loader.model self.assertEquals(model.constraints.count(), 3) self.assertIsNotNone(model.constraints.get("IUC2d")) self.assertItemsEqual(loader.omissions, ["Exclusion constraint EXC1"]) def test_deontic_cardinality(self): """ Test that deontic modality is recognized for cardinality constraints.""" fname = TestDataLocator.path("deontic_cardinality.orm") loader = NormaLoader(fname, deontic=False) model = loader.model # Only the unary IUC should not be ignored self.assertEquals(model.constraints.count(), 1) self.assertIsNotNone(model.constraints.get("IUC_unary")) def test_join_rule_with_no_join_path(self): """ Test <JoinRule> not followed by a <JoinPath>. """ fname = TestDataLocator.path("join_rule_no_join_path.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("ExternalUniquenessConstraint1") self.assertIsNone(cons) expected = "Uniqueness constraint ExternalUniquenessConstraint1 because its join path does not have exactly one JoinPath node." self.assertEquals(loader.omissions, [expected]) def test_join_rule_with_subquery(self): """ Test <JoinRule> that contains a <SubQueries> node. """ fname = TestDataLocator.path("join_rule_with_subquery.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FC1_with_subquery") self.assertIsNone(cons) expected = "Frequency constraint FC1_with_subquery because its join path has a JoinPath node with an unsupported child node: Subqueries." self.assertEquals(loader.omissions, [expected]) def test_join_rule_with_no_role_path(self): """ Test <JoinRule> that contains no <RolePath> node. """ fname = TestDataLocator.path("join_rule_no_role_path.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = "Frequency constraint FrequencyConstraint1 because its join path does not have exactly one RolePath node." self.assertEquals(loader.omissions, [expected]) def test_join_rule_with_unsupported_splits(self): """ Test <JoinRule> that contains negated split paths and combination operators other than AND. """ fname = TestDataLocator.path("join_rule_negated_split.orm") loader = NormaLoader(fname) model = loader.model cons_neg = model.constraints.get("EUC_negated") cons_or = model.constraints.get("EUC_or") self.assertIsNone(cons_neg) self.assertIsNone(cons_or) expected = [ "Uniqueness constraint EUC_negated because its join path has a negated path split.", "Uniqueness constraint EUC_or because its join path combines paths with an operator other than AND." ] self.assertEquals(loader.omissions, expected) def test_subpath_with_bad_child_node(self): """ Test <SubPath> node with bad child node. Specifically, this test case has a nested <SubPaths> node under a <SubPath>, which we do not support. """ fname = TestDataLocator.path("bad_subpath_child_node.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("EUC1") self.assertIsNone(cons) expected = [ "Uniqueness constraint EUC1 because its join path has a SubPaths node with an unsupported child node: BadSubPath." ] self.assertEquals(loader.omissions, expected) def test_pathed_roles_with_bad_child_node(self): """ Test <PathedRoles> node with bad child node. """ fname = TestDataLocator.path("join_rule_pathed_roles_bad_child.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = [ "Frequency constraint FrequencyConstraint1 because its join path has a PathedRoles node with an unsupported child node: PathedRole2." ] self.assertEquals(loader.omissions, expected) def test_join_rule_covering_implicit_roles(self): """ Test <PathedRoles> node that include implicit roles. """ fname = TestDataLocator.path("join_rule_covering_implicit_roles.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("EUC1") self.assertIsNone(cons) # Not loaded because it covers implicit roles expected = [ "Uniqueness constraint EUC1 because its join path includes an implicit role." ] self.assertEquals(loader.omissions, expected) def test_pathed_role_with_value_restriction_child_node(self): """ Test <PathedRole> node with bad child node. """ fname = TestDataLocator.path( "join_rule_pathed_role_with_value_restriction.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = [ "Frequency constraint FrequencyConstraint1 because its join path has a PathedRole node with an unsupported child node: ValueRestriction." ] self.assertEquals(loader.omissions, expected) def test_pathed_role_with_outer_join(self): """ Test <PathedRole> node with outer join. """ fname = TestDataLocator.path( "join_rule_pathed_role_with_outer_join.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = [ "Frequency constraint FrequencyConstraint1 because its join path includes an outer join." ] self.assertEquals(loader.omissions, expected) def test_pathed_role_with_negated_join(self): """ Test <PathedRole> node with negated join. """ fname = TestDataLocator.path( "join_rule_pathed_role_with_negated_join.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = [ "Frequency constraint FrequencyConstraint1 because its join path includes a negated role." ] self.assertEquals(loader.omissions, expected) def test_valid_linear_join_path(self): """ Test valid linear join path. """ fname = TestDataLocator.path("join_rule_valid_linear_path.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNotNone(cons) self.assertIsNotNone(cons.covers.join_path) self.assertEquals(loader.omissions, []) path = cons.covers.join_path f1 = model.fact_types.get("AHasB") f2 = model.fact_types.get("BHasC") f3 = model.fact_types.get("CHasD") self.assertEquals(path.fact_types, [f1, f2, f3]) self.assertEquals(path.joins, [(f1.roles[1], f2.roles[0]), (f2.roles[1], f3.roles[0])]) def test_role_path_unexpected_child(self): """ Test <RolePath> node with unexpected child node. """ fname = TestDataLocator.path( "join_rule_with_subquery_parameter_inputs.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("FrequencyConstraint1") self.assertIsNone(cons) expected = ["Frequency constraint FrequencyConstraint1 because its join path has " \ "a RolePath node with an unsupported child node: SubqueryParameterInputs."] self.assertEquals(loader.omissions, expected) def test_valid_branching_join_path(self): """ Test valid branching join path. """ fname = TestDataLocator.path("join_rule_valid_branching_path.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("EUC1") self.assertIsNotNone(cons) self.assertIsNotNone(cons.covers.join_path) self.assertEquals(loader.omissions, []) path = cons.covers.join_path f1 = model.fact_types.get("AHasD") f2 = model.fact_types.get("BHasC") f3 = model.fact_types.get("CHasD") f4 = model.fact_types.get("EHasD") self.assertEquals(path.fact_types, [f1, f3, f2, f4]) self.assertEquals(path.joins, [(f1.roles[1], f3.roles[1]), (f3.roles[0], f2.roles[1]), (f1.roles[1], f4.roles[1])]) def test_valid_complex_branching_join_path(self): """ Test valid complex branching join path. """ fname = TestDataLocator.path( "join_rule_valid_complex_branching_path.orm") loader = NormaLoader(fname) model = loader.model cons = model.constraints.get("EUC1") self.assertIsNotNone(cons) self.assertIsNotNone(cons.covers.join_path) self.assertEquals(loader.omissions, []) path = cons.covers.join_path DHasE = model.fact_types.get("DHasE") EHasB = model.fact_types.get("EHasB") BHasC = model.fact_types.get("BHasC") FHasG = model.fact_types.get("FHasG") GHasB = model.fact_types.get("GHasB") HHasG = model.fact_types.get("HHasG") self.assertEquals(path.fact_types, [DHasE, EHasB, GHasB, FHasG, HHasG, BHasC]) self.assertEquals(path.joins, [(DHasE.roles[1], EHasB.roles[0]), (EHasB.roles[1], GHasB.roles[1]), (GHasB.roles[0], FHasG.roles[1]), (GHasB.roles[0], HHasG.roles[1]), (EHasB.roles[1], BHasC.roles[0])]) def test_load_equality_constraint(self): """ Test loading of equality constraint. """ fname = TestDataLocator.path("equality_four_role.orm") loader = NormaLoader(fname) model = loader.model self.assertItemsEqual(loader.omissions, []) cons_list = model.constraints.of_type(Constraint.EqualityConstraint) self.assertEquals(3, len(cons_list)) eq1 = model.constraints.get("EQ") eq2 = model.constraints.get("EQ2") eq3 = model.constraints.get("EQ3") self.assertTrue(isinstance(eq1, Constraint.EqualityConstraint)) self.assertTrue(isinstance(eq2, Constraint.EqualityConstraint)) self.assertTrue(isinstance(eq3, Constraint.EqualityConstraint)) obj = model.object_types.get("A") self.assertEquals(eq1.superset, [obj.roles[0]]) self.assertEquals(eq1.subset, [obj.roles[1]]) self.assertEquals(eq1.covers, eq1.subset + eq1.superset) self.assertEquals(eq2.superset, [obj.roles[0]]) self.assertEquals(eq2.subset, [obj.roles[2]]) self.assertEquals(eq2.covers, eq2.subset + eq2.superset) self.assertEquals(eq3.superset, [obj.roles[0]]) self.assertEquals(eq3.subset, [obj.roles[3]]) self.assertEquals(eq3.covers, eq3.subset + eq3.superset)
class TestORMMinusModel(TestCase): """ Unit tests for the ORMMinusModel module. """ def setUp(self): self.data_dir = TestDataLocator.get_data_dir() self.maxDiff = None # Log capturing self.log = LogCapture() self.log.logformat = '%(levelname)s: %(message)s' self.log.begin() fname = os.path.join(self.data_dir, "paper_has_author.orm") model = NormaLoader(fname).model self.paper_has_author = ORMMinusModel(model=model) self.solution1 = self.paper_has_author.solution def test_overlapping_and_external_iuc(self): """ Test that overlapping and external IUCs are ignored. """ fname = os.path.join(self.data_dir, "overlapping_iuc.orm") model = NormaLoader(fname).model self.log.beforeTest(None) ormminus = ORMMinusModel(model=model) solution = ormminus.solution actual_vars = [var.name for var in ormminus._variables.itervalues()] expected_vars = [ "ObjectTypes.ValueType1", "ObjectTypes.ValueType2", "ObjectTypes.ValueType3", "FactTypes.ValueType1HasValueType3HasValueType2", "FactTypes.ValueType1HasValueType3HasValueType2.Roles.ValueType1", "FactTypes.ValueType1HasValueType3HasValueType2.Roles.ValueType2", "FactTypes.ValueType1HasValueType3HasValueType2.Roles.ValueType3", "FactTypes.ValueType1HasValueType3", "FactTypes.ValueType1HasValueType3.Roles.ValueType1", "FactTypes.ValueType1HasValueType3.Roles.ValueType3", "FactTypes.ValueType3HasValueType2", "FactTypes.ValueType3HasValueType2.Roles.ValueType3", "FactTypes.ValueType3HasValueType2.Roles.ValueType2", "Constraints.IUC2", "Constraints.IUC3", "Constraints.IUC4" ] self.assertItemsEqual(actual_vars, expected_vars) actual_ignored = [cons.name for cons in ormminus.ignored] expect_ignored = ["IUC1", "EUC1"] self.assertItemsEqual(actual_ignored, expect_ignored) self.assertItemsEqual(self.log.formatLogRecords(), [ "WARNING: 2 constraints were ignored while checking the model.", "INFO: Ignored UniquenessConstraint named IUC1.", "INFO: Ignored UniquenessConstraint named EUC1." ]) self.log.afterTest(None) def test_disjunctive_mandatory(self): """ Test that disjunctive mandatory constraint is ignored. """ fname = os.path.join(self.data_dir, "disjunctive_mandatory.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model) actual = [cons.name for cons in ormminus.ignored] expected = ["InclusiveOrConstraint1"] self.assertItemsEqual(actual, expected) def test_ignored_constraint(self): """ Test that appropriate constraints are ignored. """ # IMPORTANT: I expect this test to fail when I update algorithm to # work with subset constraints. At that point, CHANGE this test to # check that a different kind of constraint (e.g. Exclusion) is # ignored. That will require updating NormaLoader to load such # constraints rather than omitting them. fname = os.path.join(self.data_dir, "subset_constraint.orm") model = NormaLoader(fname, deontic=True).model ormminus = ORMMinusModel(model=model) solution = ormminus.solution actual = [cons.name for cons in ormminus.ignored] expect = [ "SubsetConstraint1", "SubsetConstraint2", "SubsetConstraint3", "SubsetConstraint4" ] self.assertItemsEqual(actual, expect) def test_ignored_value_constraint(self): """ Test that role value constraints are ignored. """ fname = os.path.join(self.data_dir, "test_value_type_value_constraint.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model) solution = ormminus.solution actual = [cons.name for cons in ormminus.ignored] expect = ["RVC_ET3", "RVC_ET4", "RVC_ET5", "RVC_VT2"] self.assertItemsEqual(actual, expect) def test_create_variables(self): """ Test creation of variables dictionary. """ actual_vars = [ var.name for var in self.paper_has_author._variables.itervalues() ] expect_vars = [ "ObjectTypes.Paper", "ObjectTypes.Author", "FactTypes.PaperHasAuthor", "FactTypes.PaperHasAuthor.Roles.R1", "FactTypes.PaperHasAuthor.Roles.R2", "Constraints.FrequencyConstraint1", "Constraints.InternalUniquenessConstraint1" ] self.assertItemsEqual(actual_vars, expect_vars) def test_create_inequalities_1(self): """ Test creation of inequalities for Paper Has Author model. """ actual = set( [ineq.tostring() for ineq in self.paper_has_author._ineqsys]) expect = [ "FactTypes.PaperHasAuthor.Roles.R1 <= ObjectTypes.Paper", "FactTypes.PaperHasAuthor.Roles.R1 <= FactTypes.PaperHasAuthor", "FactTypes.PaperHasAuthor.Roles.R2 <= ObjectTypes.Author", "FactTypes.PaperHasAuthor.Roles.R2 <= FactTypes.PaperHasAuthor", "ObjectTypes.Paper <= 10", "ObjectTypes.Author <= 5", "ObjectTypes.Author <= FactTypes.PaperHasAuthor.Roles.R2", "FactTypes.PaperHasAuthor <= 3 * Constraints.FrequencyConstraint1", "Constraints.FrequencyConstraint1 <= 0.5 * FactTypes.PaperHasAuthor", "Constraints.FrequencyConstraint1 <= FactTypes.PaperHasAuthor.Roles.R1", "FactTypes.PaperHasAuthor.Roles.R1 <= Constraints.FrequencyConstraint1", "FactTypes.PaperHasAuthor <= Constraints.InternalUniquenessConstraint1", "Constraints.InternalUniquenessConstraint1 <= FactTypes.PaperHasAuthor", "Constraints.InternalUniquenessConstraint1 <= FactTypes.PaperHasAuthor.Roles.R2", "FactTypes.PaperHasAuthor.Roles.R2 <= Constraints.InternalUniquenessConstraint1", "ObjectTypes.Paper <= FactTypes.PaperHasAuthor.Roles.R1", "FactTypes.PaperHasAuthor <= Constraints.FrequencyConstraint1 * Constraints.InternalUniquenessConstraint1" ] self.assertItemsEqual(actual, expect) def test_solve_1(self): """ Test solution to Paper Has Author model. """ solution = self.solution1 self.assertEquals(solution["ObjectTypes.Author"], 5) self.assertEquals(solution["ObjectTypes.Paper"], 2) self.assertEquals(solution["FactTypes.PaperHasAuthor"], 5) self.assertEquals(solution["FactTypes.PaperHasAuthor.Roles.R1"], 2) self.assertEquals(solution["FactTypes.PaperHasAuthor.Roles.R2"], 5) self.assertEquals(solution["Constraints.FrequencyConstraint1"], 2) self.assertEquals( solution["Constraints.InternalUniquenessConstraint1"], 5) def test_implicit_disjunctive_ineq(self): """ Test implicit disjunctive mandatory inequalities. """ self.log.beforeTest(None) fname = os.path.join(self.data_dir, "implicit_disjunctive_test.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model) solution = ormminus.solution actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) expect = [ "ObjectTypes.A <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.B <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.C <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.D <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.E <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.F <= " + str(ORMMinusModel.DEFAULT_SIZE), "FactTypes.ALikesA.Roles.R1 <= ObjectTypes.A", "FactTypes.ALikesA.Roles.R1 <= FactTypes.ALikesA", "FactTypes.ALikesA.Roles.R2 <= ObjectTypes.A", "FactTypes.ALikesA.Roles.R2 <= FactTypes.ALikesA", "FactTypes.ASharesB.Roles.R3 <= ObjectTypes.A", "FactTypes.ASharesB.Roles.R3 <= FactTypes.ASharesB", "FactTypes.ASharesB.Roles.R4 <= ObjectTypes.B", "FactTypes.ASharesB.Roles.R4 <= FactTypes.ASharesB", "FactTypes.AOwnsD.Roles.R5 <= ObjectTypes.A", "FactTypes.AOwnsD.Roles.R5 <= FactTypes.AOwnsD", "FactTypes.AOwnsD.Roles.R6 <= ObjectTypes.D", "FactTypes.AOwnsD.Roles.R6 <= FactTypes.AOwnsD", "FactTypes.EHasF.Roles.R7 <= ObjectTypes.E", "FactTypes.EHasF.Roles.R7 <= FactTypes.EHasF", "FactTypes.EHasF.Roles.R8 <= ObjectTypes.F", "FactTypes.EHasF.Roles.R8 <= FactTypes.EHasF", "FactTypes.ALikesA <= Constraints.IUC1", "Constraints.IUC1 <= FactTypes.ALikesA", "Constraints.IUC1 <= FactTypes.ALikesA.Roles.R1", "FactTypes.ALikesA.Roles.R1 <= Constraints.IUC1", "FactTypes.ASharesB <= Constraints.IUC2", "Constraints.IUC2 <= FactTypes.ASharesB", "Constraints.IUC2 <= FactTypes.ASharesB.Roles.R3", "FactTypes.ASharesB.Roles.R3 <= Constraints.IUC2", "FactTypes.AOwnsD <= Constraints.IUC3", "Constraints.IUC3 <= FactTypes.AOwnsD", "Constraints.IUC3 <= FactTypes.AOwnsD.Roles.R5", "FactTypes.AOwnsD.Roles.R5 <= Constraints.IUC3", "FactTypes.EHasF <= Constraints.IUC4", "Constraints.IUC4 <= FactTypes.EHasF", "Constraints.IUC4 <= FactTypes.EHasF.Roles.R7", "FactTypes.EHasF.Roles.R7 <= Constraints.IUC4", "ObjectTypes.A <= FactTypes.ALikesA.Roles.R1 + FactTypes.ALikesA.Roles.R2 + FactTypes.AOwnsD.Roles.R5 + FactTypes.ASharesB.Roles.R3", "ObjectTypes.D <= FactTypes.AOwnsD.Roles.R6", "ObjectTypes.E <= FactTypes.EHasF.Roles.R7", "ObjectTypes.F <= ObjectTypes.D", "FactTypes.ALikesA <= Constraints.IUC1 * FactTypes.ALikesA.Roles.R2", "FactTypes.ASharesB <= Constraints.IUC2 * FactTypes.ASharesB.Roles.R4", "FactTypes.AOwnsD <= Constraints.IUC3 * FactTypes.AOwnsD.Roles.R6", "FactTypes.EHasF <= Constraints.IUC4 * FactTypes.EHasF.Roles.R8" ] self.assertItemsEqual(actual, expect) self.assertItemsEqual(self.log.formatLogRecords(), []) self.log.afterTest(None) def test_implicit_disjunctive_on_entities(self): """ Test implicit disjunctive mandatory constraint inequality on entity types. """ fname = os.path.join(self.data_dir, "implicit_disjunctive_test_2.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model) solution = ormminus.solution actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) expected = [ "ObjectTypes.A <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.A_id <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.Boolean <= 2", "FactTypes.AHasBoolean.Roles.R1 <= ObjectTypes.A", "FactTypes.AHasBoolean.Roles.R1 <= FactTypes.AHasBoolean", "FactTypes.AHasBoolean.Roles.R2 <= ObjectTypes.Boolean", "FactTypes.AHasBoolean.Roles.R2 <= FactTypes.AHasBoolean", "FactTypes.AHasAId.Roles.R1 <= ObjectTypes.A", "FactTypes.AHasAId.Roles.R1 <= FactTypes.AHasAId", "FactTypes.AHasAId.Roles.R2 <= ObjectTypes.A_id", "FactTypes.AHasAId.Roles.R2 <= FactTypes.AHasAId", "FactTypes.AHasAId <= Constraints.IUC1", "Constraints.IUC1 <= FactTypes.AHasAId", "Constraints.IUC1 <= FactTypes.AHasAId.Roles.R2", "FactTypes.AHasAId.Roles.R2 <= Constraints.IUC1", "FactTypes.AHasAId <= Constraints.IUC2", "Constraints.IUC2 <= FactTypes.AHasAId", "Constraints.IUC2 <= FactTypes.AHasAId.Roles.R1", "FactTypes.AHasAId.Roles.R1 <= Constraints.IUC2", "FactTypes.AHasBoolean <= Constraints.IUC3", "Constraints.IUC3 <= FactTypes.AHasBoolean", "Constraints.IUC3 <= FactTypes.AHasBoolean.Roles.R2", "FactTypes.AHasBoolean.Roles.R2 <= Constraints.IUC3", "ObjectTypes.A <= FactTypes.AHasAId.Roles.R1", "ObjectTypes.A <= FactTypes.AHasBoolean.Roles.R1", "ObjectTypes.A_id <= FactTypes.AHasAId.Roles.R2", "ObjectTypes.Boolean <= FactTypes.AHasBoolean.Roles.R2", "FactTypes.AHasAId <= Constraints.IUC1 * Constraints.IUC2", "FactTypes.AHasBoolean <= Constraints.IUC3 * FactTypes.AHasBoolean.Roles.R1" ] self.assertItemsEqual(actual, expected) self.assertEquals(solution["ObjectTypes.A"], 2) def test_idmc_with_disjunctive_ref_old_approach(self): """ Test implicit disjunctive mandatory constraint inequality when entity type has disjunctive ref scheme, using the old approach. """ fname = os.path.join(self.data_dir, "disjunctive_reference_scheme.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model, experimental=False) # Confirm IDMC inequality includes all roles played by A actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) euc = model.constraints.get("EUC") a = model.object_types.get("A") self.assertIn( "ObjectTypes.A <= FactTypes.AHasB.Roles.A + FactTypes.AHasC.Roles.A", actual) self.assertIsNone(a.identifying_constraint) self.assertIsNone(euc.identifier_for) self.assertEquals(a.ref_roles, []) def test_idmc_with_disjunctive_ref_new_approach(self): """ Test implicit disjunctive mandatory constraint inequality when entity type has disjunctive ref scheme, using the new approach. """ fname = os.path.join(self.data_dir, "disjunctive_reference_scheme.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model, experimental=True) # Confirm there is *no* IDMC inequality for A because all of its roles # are reference roles actual = set([ ineq.tostring() for ineq in ormminus._ineqsys if ineq.tostring().startswith("ObjectTypes.A <=") ]) expected = [ "ObjectTypes.A <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.A <= FactTypes.AHasB.Roles.A", "ObjectTypes.A <= FactTypes.AHasC.Roles.A" ] self.assertItemsEqual(actual, expected) # Assert EUC removed and B's role is covered by IUC self.assertIsNone(model.constraints.get("EUC")) buniq = model.object_types.get("B").roles[0].covered_by[0] self.assertTrue(isinstance(buniq, Constraint.UniquenessConstraint)) self.assertTrue(buniq.simple) # Confirm all roles of A are mandatory now a = model.object_types.get("A") role1 = model.fact_types.get("AHasB").roles[0] role2 = model.fact_types.get("AHasC").roles[0] self.assertTrue(role1.mandatory) self.assertTrue(role2.mandatory) # Confirm A's reference roles are intact self.assertIs(a.identifying_constraint, None) self.assertItemsEqual(a.ref_roles, [role1, role2]) def test_unsat_smarag_1(self): """ Test 1st unsatisfiable model provided by Smaragdakis. """ fname = os.path.join(self.data_dir, "unsat_smarag_1.orm") model = NormaLoader(fname).model self.assertIsNone(ORMMinusModel(model=model).solution) def test_unsat_smarag_2(self): """ Test 2nd unsatisfiable model provided by Smaragdakis. """ fname = os.path.join(self.data_dir, "unsat_smarag_2.orm") model = NormaLoader(fname).model self.assertIsNone(ORMMinusModel(model=model).solution) def test_unsat_smarag_3(self): """ Test 3rd unsatisfiable model provided by Smaragdakis. """ fname = os.path.join(self.data_dir, "unsat_smarag_3.orm") model = NormaLoader(fname).model self.assertIsNone(ORMMinusModel(model=model).solution) def test_unsat_overlapping_iuc(self): """ Test case where OverlappingIFCTransform makes model unsat. """ fname = os.path.join(self.data_dir, "overlapping_iuc_unsat_if_strengthened.orm") model = NormaLoader(fname).model self.assertIsNotNone( ORMMinusModel(model=model, experimental=False).solution) model = NormaLoader(fname).model self.assertIsNone( ORMMinusModel(model=model, experimental=True).solution) def test_unsat_euc_strengthening(self): """ Test case where EUC Strengthening makes model unsat. """ fname = os.path.join(self.data_dir, "euc_strengthening_unsat.orm") model = NormaLoader(fname).model self.assertIsNotNone( ORMMinusModel(model=model, experimental=False).solution) model = NormaLoader(fname).model self.assertIsNone( ORMMinusModel(model=model, experimental=True).solution) def test_ubound_on_object_types(self): """ Test upper bound on object type variables. """ fname = os.path.join(self.data_dir, "data_types.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model, ubound=sys.maxsize) solution = ormminus.solution bool_obj = model.object_types.get("B") bool_var = ormminus._variables[bool_obj] self.assertEquals(bool_var.upper, 2) int_obj = model.object_types.get("D") int_var = ormminus._variables[int_obj] self.assertEquals(int_var.upper, sys.maxsize) time_obj = model.object_types.get("J") time_var = ormminus._variables[time_obj] self.assertEquals(time_var.upper, 60 * 24) def test_fact_type_parts(self): """ Test that fact type parts (e.g. roles vs role sequences) are correctly identified. """ fname = os.path.join(self.data_dir, "fact_type_parts.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model=model, ubound=5) fact_type = model.fact_types.get("V1HasV2HasV3") role1, role2, role3 = fact_type.roles self.assertItemsEqual(ormminus.get_parts(fact_type), [role1, role2, role3]) fact_type = model.fact_types.get("V4HasV5") role1, role2 = fact_type.roles cons = model.constraints.get("IUC4") self.assertItemsEqual(ormminus.get_parts(fact_type), [cons, role2]) fact_type = model.fact_types.get("V6HasV7") cons = model.constraints.get("IUC5") self.assertItemsEqual(ormminus.get_parts(fact_type), [cons]) fact_type = model.fact_types.get("V8HasV9") cons = model.constraints.get("IFC1") self.assertItemsEqual(ormminus.get_parts(fact_type), [cons]) fact_type = model.fact_types.get("Seven_ary") roles = fact_type.roles iuc = model.constraints.get("IUC11") ifc = model.constraints.get("IFC2") self.assertItemsEqual(ormminus.get_parts(fact_type), [iuc, ifc, roles[3], roles[6]]) def test_cardinality_inequalities(self): """ Test that correct cardinality constraint inequalities are generated. """ self.log.beforeTest(None) fname = os.path.join(self.data_dir, "test_cardinality_constraint.orm") model = NormaLoader(fname, deontic=True).model ormminus = ORMMinusModel(model) actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) expected = [ "ObjectTypes.A <= " + str(ORMMinusModel.DEFAULT_SIZE), "ObjectTypes.B <= " + str(ORMMinusModel.DEFAULT_SIZE), "FactTypes.AExists.Roles.A <= ObjectTypes.A", "FactTypes.AExists.Roles.A <= FactTypes.AExists", "FactTypes.AExists <= Constraints.IUC1", "Constraints.IUC1 <= FactTypes.AExists", "Constraints.IUC1 <= FactTypes.AExists.Roles.A", "FactTypes.AExists.Roles.A <= Constraints.IUC1", "ObjectTypes.A <= FactTypes.AExists.Roles.A", "FactTypes.BDances.Roles.B <= ObjectTypes.B", "FactTypes.BDances.Roles.B <= FactTypes.BDances", "FactTypes.BDances <= Constraints.IUC2", "Constraints.IUC2 <= FactTypes.BDances", "Constraints.IUC2 <= FactTypes.BDances.Roles.B", "FactTypes.BDances.Roles.B <= Constraints.IUC2", "FactTypes.BHopes.Roles.B <= ObjectTypes.B", "FactTypes.BHopes.Roles.B <= FactTypes.BHopes", "FactTypes.BHopes <= Constraints.IUC3", "Constraints.IUC3 <= FactTypes.BHopes", "Constraints.IUC3 <= FactTypes.BHopes.Roles.B", "FactTypes.BHopes.Roles.B <= Constraints.IUC3", "ObjectTypes.B <= FactTypes.BDances.Roles.B + FactTypes.BHopes.Roles.B", "0 <= ObjectTypes.A", "ObjectTypes.A <= 4", "4 <= FactTypes.AExists.Roles.A", "FactTypes.AExists.Roles.A <= 7", "2 <= ObjectTypes.B", "4 <= FactTypes.BHopes.Roles.B", "FactTypes.BHopes.Roles.B <= 4" ] self.assertItemsEqual(actual, expected) self.assertItemsEqual(ormminus.ignored, [model.constraints.get("CC5")]) self.assertItemsEqual(self.log.formatLogRecords(), [ "WARNING: 1 constraint was ignored while checking the model.", "INFO: Ignored CardinalityConstraint named CC5." ]) self.log.afterTest(None) def test_unbounded_freq_constraint(self): """ Test a model with an unbounded frequency constraint. """ fname = os.path.join(self.data_dir, "unbounded_frequency_constraint.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model) cons = model.constraints.get("IFC1") self.assertEquals(cons.max_freq, float('inf')) self.assertEquals(cons.min_freq, 5) solution = ormminus.solution size = ORMMinusModel.DEFAULT_SIZE self.assertEquals(solution["Constraints.IFC1"], size / 5) self.assertEquals(solution["FactTypes.AHasB"], size) def test_objectification_inequalities(self): """ Test a model with objectifications. """ fname = os.path.join(self.data_dir, "objectification.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=10) # Confirm NORMA doesn't copy the cardinality constraint to the fact type self.assertEquals( len(model.constraints.of_type(Constraint.CardinalityConstraint)), 1) # Check for existence of expected inequalities actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) self.assertIn("ObjectTypes.AHasB <= FactTypes.AHasB", actual) self.assertIn("FactTypes.AHasB <= ObjectTypes.AHasB", actual) self.assertIn("ObjectTypes.ALikesB <= FactTypes.ALikesB", actual) self.assertIn("FactTypes.ALikesB <= ObjectTypes.ALikesB", actual) self.assertIn("ObjectTypes.AEnjoysB <= FactTypes.AEnjoysB", actual) self.assertIn("FactTypes.AEnjoysB <= ObjectTypes.AEnjoysB", actual) # Check that solution has expected relations self.assertEquals(ormminus.solution["ObjectTypes.AHasB"], ormminus.solution["FactTypes.AHasB"]) self.assertEquals(ormminus.solution["ObjectTypes.AHasB"], 10) self.assertEquals(ormminus.solution["ObjectTypes.ALikesB"], ormminus.solution["FactTypes.ALikesB"]) self.assertEquals(ormminus.solution["ObjectTypes.ALikesB"], 5) self.assertEquals(ormminus.solution["ObjectTypes.AEnjoysB"], ormminus.solution["FactTypes.AEnjoysB"]) self.assertEquals(ormminus.solution["ObjectTypes.AEnjoysB"], 3) def test_unsat_role_and_value_type_value_constraint(self): """ Test unsatisfiable combination of role and value type value constraint. """ fname = os.path.join( self.data_dir, "role_value_constraint_and_type_value_constraint.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=10) self.assertItemsEqual(ormminus.ignored, []) a_id = model.object_types.get("A_id") cc1 = model.constraints.get("CC1") rvc1 = model.constraints.get("RVC1") vc1 = model.constraints.get("VC1") value_cons = model.constraints.of_type(Constraint.ValueConstraint) self.assertEquals(len(value_cons), 2) self.assertItemsEqual(value_cons, [rvc1, vc1]) self.assertEquals(rvc1.covers, [a_id]) self.assertEquals(vc1.covers, [a_id]) self.assertItemsEqual(a_id.covered_by, [rvc1, vc1, cc1]) # Key test 1: domain of a_id is intersection of two value constraints self.assertItemsEqual(a_id.domain.draw(10), [1, 2]) # Key test 2: model is unsat due to cardinality constraint self.assertIsNone(ormminus.solution) def test_subtype_inequalities(self): """ Test that expected subtype inequalities are created. """ fname = TestDataLocator.path("value_constraints_on_subtypes.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=30) actual = set([ineq.tostring() for ineq in ormminus._ineqsys]) self.assertIn("ObjectTypes.J <= ObjectTypes.I", actual) self.assertIn("ObjectTypes.L <= ObjectTypes.J", actual) self.assertIn("ObjectTypes.K <= ObjectTypes.I", actual) self.assertIn("ObjectTypes.M <= ObjectTypes.J", actual) self.assertIn("ObjectTypes.M <= ObjectTypes.K", actual) self.assertEquals(ormminus.solution["ObjectTypes.I"], 21) self.assertEquals(ormminus.solution["ObjectTypes.J"], 21) self.assertEquals(ormminus.solution["ObjectTypes.K"], 11) self.assertEquals(ormminus.solution["ObjectTypes.M"], 11) def test_unsat_subtype_with_value_constraint(self): """ Test a model that is unsatisfiable because the intersection of the value constraints for the root type and subtype is empty. """ fname = TestDataLocator.path("unsat_subtype_with_value_constraint.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=30) self.assertIsNone(ormminus.solution) def test_unsat_subtype_due_to_card_constraint(self): """ Test a model that is unsatisfiable because the cardinality of the root type is less than required for one of the subtypes. """ fname = TestDataLocator.path( "unsat_subtype_due_to_card_constraint.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=30) self.assertIsNone(ormminus.solution) def test_sat_with_absorption(self): """ Test a satisfiable model with a compound reference scheme. """ fname = TestDataLocator.path("absorption_valid_simple.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=10) self.assertEquals(ormminus.solution["FactTypes.EUC1"], 10) def test_unsat_absorption(self): """ Test an unsatisfiable model with a compound reference scheme. """ fname = TestDataLocator.path("absorption_unsat.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=10) self.assertIsNone(ormminus.solution) def test_unsat_subset(self): """ Test a model that is unsatisfiable due to a subset constraint. """ fname = TestDataLocator.path("subset_unsat.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=True) self.assertIsNone(ormminus.solution) model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=False) self.assertIsNotNone(ormminus.solution) def test_sat_subset(self): """ Test a model that is satisfiable with subset constraints. """ fname = TestDataLocator.path("subset_multiple_roots.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=True) solution = ormminus.solution self.assertIsNotNone(solution) self.assertItemsEqual(ormminus.ignored, []) self.assertEquals(solution["FactTypes.ALikesB.Roles.A"], 7) self.assertEquals(solution["FactTypes.CLikesA.Roles.A"], 7) self.assertEquals(solution["FactTypes.ASmokes.Roles.A"], 7) self.assertEquals(solution["FactTypes.AExists.Roles.A"], 6) self.assertEquals(solution["FactTypes.AHasB.Roles.A"], 6) self.assertEquals(solution["ObjectTypes.A"], 7) self.assertEquals(solution["FactTypes.ALikesB.Roles.B"], 7) self.assertEquals(solution["FactTypes.AHasB.Roles.B"], 6) self.assertEquals(solution["ObjectTypes.B"], 7) self.assertEquals(solution["ObjectTypes.C"], 100) # Check inequalities actual = [ineq.tostring() for ineq in ormminus._ineqsys] # Check IDMC for A filt = lambda x: x.startswith("ObjectTypes.A <=") self.assertItemsEqual(filter(filt, actual), [ "ObjectTypes.A <= 100", "ObjectTypes.A <= FactTypes.ALikesB.Roles.A" ]) # Check IDMC for B filt = lambda x: x.startswith("ObjectTypes.B <=") self.assertItemsEqual(filter(filt, actual), [ "ObjectTypes.B <= 100", "ObjectTypes.B <= 7", "ObjectTypes.B <= FactTypes.ALikesB.Roles.B + FactTypes.BExists.Roles.B" ]) # Confirm R < S inequalities present self.assertIn("FactTypes.AHasB.Roles.A <= FactTypes.ALikesB.Roles.A", actual) self.assertIn("FactTypes.AHasB.Roles.B <= FactTypes.ALikesB.Roles.B", actual) def test_ignored_subset(self): """ Test a model with an ignored subset constraint. """ fname = TestDataLocator.path("subset_with_incompat_superset.orm") model = NormaLoader(fname).model subset = model.constraints.get("SUB1") ormminus = ORMMinusModel(model, ubound=100, experimental=True) solution = ormminus.solution self.assertIsNotNone(solution) self.assertItemsEqual(ormminus.ignored, [subset]) def test_unsat_equality(self): """ Test a model that is unsatisfiable due to an equality constraint. """ fname = TestDataLocator.path("equality_unsat.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=True) self.assertIsNone(ormminus.solution) actual = [ineq.tostring() for ineq in ormminus._ineqsys] self.assertIn("FactTypes.ALovesB.Roles.A <= FactTypes.AHasB.Roles.A", actual) self.assertIn("FactTypes.AHasB.Roles.A <= FactTypes.ALovesB.Roles.A", actual) model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=False) self.assertIsNotNone(ormminus.solution) def test_unsat_join_equality(self): """ Test a model that is unsatisfiable due to a join equality constraint. """ fname = TestDataLocator.path("join_equality_unsat.orm") model = NormaLoader(fname).model ormminus = ORMMinusModel(model, ubound=100, experimental=True) self.assertEquals(ormminus.ignored, []) self.assertIsNone(ormminus.solution) model = NormaLoader(fname).model eq = model.constraints.get("EQ") ormminus = ORMMinusModel(model, ubound=100, experimental=False) self.assertEquals(ormminus.ignored, [eq]) self.assertIsNotNone(ormminus.solution)