def test_object_caching(self): # Creating a Feature with the same name as an existing # Feature must return the existing Feature, if both are # associated with the same BinaryFeaturesModel. feature1 = BaseFeature(self.bfm, 'voiced') feature2 = BaseFeature(self.bfm, 'voiced') self.assertEqual(feature1, feature2) feature3 = BaseFeature(self.bfm, 'consonantal') self.assertNotEqual(feature1, feature3) bfm2 = BinaryFeaturesModel() feature4 = BaseFeature(bfm2, 'voiced') self.assertNotEqual(feature1, feature4) # It is an error to create a Feature with the same IPA form # but of a different type (subclass). self.assertRaises(InvalidFeatureError, SuprasegmentalFeature, self.bfm, 'voiced') # Initialisation of a Feature should happen only once. character1 = BaseCharacter(self.bfm, 'a') character1.set_feature_value(feature1, HAS_FEATURE) self.assertEqual(feature1.get_value_characters(HAS_FEATURE), set([character1])) feature5 = BaseFeature(self.bfm, 'voiced') self.assertEqual(feature1.get_value_characters(HAS_FEATURE), set([character1])) feature6 = SuprasegmentalFeature(self.bfm, 'syllabic') character2 = SuprasegmentalCharacter(self.bfm, 'b') character2.set_feature_value(feature6, HAS_FEATURE) self.assertEqual(feature6.get_value_characters(HAS_FEATURE), set([character2])) feature7 = SuprasegmentalFeature(self.bfm, 'syllabic') self.assertEqual(feature6.get_value_characters(HAS_FEATURE), set([character2]))
def setUp (self): self.bfm = BinaryFeaturesModel() self.anterior = BaseFeature(self.bfm, 'anterior') self.back = BaseFeature(self.bfm, 'back') self.coronal = BaseFeature(self.bfm, 'coronal') self.long = BaseFeature(self.bfm, 'long') self.voiced = BaseFeature(self.bfm, 'voiced')
def _populate_binary_features_model(self): self.bfm = BinaryFeaturesModel() self.anterior = BaseFeature(self.bfm, 'anterior') self.back = BaseFeature(self.bfm, 'back') self.coronal = BaseFeature(self.bfm, 'coronal') self.long = BaseFeature(self.bfm, 'long') self.voiced = BaseFeature(self.bfm, 'voiced') self.p = BaseCharacter(self.bfm, 'p') self.p.set_feature_value(self.anterior, HAS_FEATURE) self.p.set_feature_value(self.back, NOT_HAS_FEATURE) self.p.set_feature_value(self.coronal, NOT_HAS_FEATURE) self.p.set_feature_value(self.long, NOT_HAS_FEATURE) self.p.set_feature_value(self.voiced, NOT_HAS_FEATURE) self.b = BaseCharacter(self.bfm, 'b') self.b.set_feature_value(self.anterior, HAS_FEATURE) self.b.set_feature_value(self.back, NOT_HAS_FEATURE) self.b.set_feature_value(self.coronal, NOT_HAS_FEATURE) self.b.set_feature_value(self.long, NOT_HAS_FEATURE) self.b.set_feature_value(self.voiced, HAS_FEATURE) self.t = BaseCharacter(self.bfm, 't') self.t.set_feature_value(self.anterior, HAS_FEATURE) self.t.set_feature_value(self.back, NOT_HAS_FEATURE) self.t.set_feature_value(self.coronal, HAS_FEATURE) self.t.set_feature_value(self.long, NOT_HAS_FEATURE) self.t.set_feature_value(self.voiced, NOT_HAS_FEATURE) self.d = BaseCharacter(self.bfm, 'd') self.d.set_feature_value(self.anterior, HAS_FEATURE) self.d.set_feature_value(self.back, NOT_HAS_FEATURE) self.d.set_feature_value(self.coronal, HAS_FEATURE) self.d.set_feature_value(self.long, NOT_HAS_FEATURE) self.d.set_feature_value(self.voiced, HAS_FEATURE) self.q = BaseCharacter(self.bfm, 'q') self.q.set_feature_value(self.anterior, NOT_HAS_FEATURE) self.q.set_feature_value(self.back, HAS_FEATURE) self.q.set_feature_value(self.coronal, NOT_HAS_FEATURE) self.q.set_feature_value(self.long, NOT_HAS_FEATURE) self.q.set_feature_value(self.voiced, NOT_HAS_FEATURE) self.ring = DiacriticCharacter(self.bfm, '̥') self.ring.set_feature_value(self.anterior, INAPPLICABLE_FEATURE) self.ring.set_feature_value(self.back, INAPPLICABLE_FEATURE) self.ring.set_feature_value(self.coronal, INAPPLICABLE_FEATURE) self.ring.set_feature_value(self.long, INAPPLICABLE_FEATURE) self.ring.set_feature_value(self.voiced, NOT_HAS_FEATURE) self.caret = DiacriticCharacter(self.bfm, '̬') self.caret.set_feature_value(self.anterior, INAPPLICABLE_FEATURE) self.caret.set_feature_value(self.back, INAPPLICABLE_FEATURE) self.caret.set_feature_value(self.coronal, INAPPLICABLE_FEATURE) self.caret.set_feature_value(self.long, INAPPLICABLE_FEATURE) self.caret.set_feature_value(self.voiced, HAS_FEATURE) self.ː = SpacingCharacter(self.bfm, 'ː') self.ː.set_feature_value(self.anterior, INAPPLICABLE_FEATURE) self.ː.set_feature_value(self.back, INAPPLICABLE_FEATURE) self.ː.set_feature_value(self.coronal, INAPPLICABLE_FEATURE) self.ː.set_feature_value(self.long, HAS_FEATURE) self.ː.set_feature_value(self.voiced, INAPPLICABLE_FEATURE)
def test_feature_value_illegal(self): # The Feature must belong to the same BinaryFeaturesModel as # the Character. character = SuprasegmentalCharacter(self.bfm, 'a') bfm2 = BinaryFeaturesModel() feature1 = SuprasegmentalFeature(bfm2, 'voiced') self.assertRaises(MismatchedModelsError, character.get_feature_value, feature1) self.assertRaises(MismatchedModelsError, character.set_feature_value, feature1, HAS_FEATURE) # The Feature's type (subclass) must be appropriate to the # type (subclass) of the Character. feature2 = BaseFeature(self.bfm, 'stressed') self.assertRaises(MismatchedTypesError, character.get_feature_value, feature2) self.assertRaises(MismatchedTypesError, character.set_feature_value, feature2, HAS_FEATURE) # A SuprasegmentalCharacter must have a feature value for all # SuprasegmentalFeatures. feature3 = SuprasegmentalFeature(self.bfm, 'syllabic') self.assertRaises(InvalidCharacterError, character.get_feature_value, feature3) # Only certain values are permitted. self.assertRaises(IllegalArgumentError, character.set_feature_value, feature3, True)
def test_value_characters (self): feature = BaseFeature(self.bfm, 'voiced') self.assertEqual(feature.get_value_characters(HAS_FEATURE), set()) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set()) character1 = BaseCharacter(self.bfm, 'a') character1.set_feature_value(feature, HAS_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set([character1])) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set()) character1.set_feature_value(feature, NOT_HAS_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set()) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set([character1])) character2 = DiacriticCharacter(self.bfm, 'b') character2.set_feature_value(feature, HAS_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set([character2])) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set([character1])) character2.set_feature_value(feature, INAPPLICABLE_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set()) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set([character1])) character3 = SpacingCharacter(self.bfm, 'c') character3.set_feature_value(feature, NOT_HAS_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set()) self.assertEqual(feature.get_value_characters(NOT_HAS_FEATURE), set([character1, character3]))
def test_delete (self): Feature._cache = {} self.assertEqual(len(Feature._cache), 0) feature = BaseFeature(self.bfm, 'voiced') character = BaseCharacter(self.bfm, 'a') character.set_feature_value(feature, HAS_FEATURE) self.assertEqual(len(Feature._cache), 1) self.assertEqual(character.get_feature_value(feature), HAS_FEATURE) feature.delete() self.assertEqual(len(Feature._cache), 0) self.assertRaises(MismatchedModelsError, character.get_feature_value, feature) feature = BaseFeature(self.bfm, 'voiced') character = SpacingCharacter(self.bfm, 'b') self.assertEqual(len(Feature._cache), 1) feature.delete() self.assertEqual(len(Feature._cache), 0)
def test_delete(self): self.assertEqual(len(Character._cache), 0) feature = BaseFeature(self.bfm, 'voiced') character = BaseCharacter(self.bfm, 'a') character.set_feature_value(feature, HAS_FEATURE) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set([character])) self.assertEqual(len(Character._cache), 1) character.delete() self.assertEqual(len(Character._cache), 0) self.assertEqual(feature.get_value_characters(HAS_FEATURE), set())
def test_normalised_form(self): feature1 = BaseFeature(self.bfm, 'anterior') character1 = DiacriticCharacter(self.bfm, 'a') character1.set_feature_value(feature1, HAS_FEATURE) self.assertEqual(character1.normalised_form, NormalisedForm('{0}{1}'.format(BNFM, HAS_FEATURE))) # Adding a feature changes the normalised form. feature2 = BaseFeature(self.bfm, 'dental') character1.set_feature_value(feature2, INAPPLICABLE_FEATURE) self.assertEqual( character1.normalised_form, NormalisedForm('{0}{1}{2}'.format(BNFM, HAS_FEATURE, INAPPLICABLE_FEATURE))) # The order of the normalised form feature values is # alphabetical by feature name. feature3 = BaseFeature(self.bfm, 'consonantal') character1.set_feature_value(feature3, NOT_HAS_FEATURE) self.assertEqual( character1.normalised_form, NormalisedForm('{0}{1}{2}{3}'.format(BNFM, HAS_FEATURE, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE))) # Renaming a feature may change the normalised form. feature1.name = 'vocalic' self.assertEqual( character1.normalised_form, NormalisedForm('{0}{1}{2}{3}'.format(BNFM, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE, HAS_FEATURE))) # Changing a feature value changes the normalised form. character1.set_feature_value(feature1, NOT_HAS_FEATURE) self.assertEqual( character1.normalised_form, NormalisedForm('{0}{1}{2}{1}'.format(BNFM, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE))) # Removing a feature value changes the normalised form. feature2.delete() self.assertEqual( character1.normalised_form, NormalisedForm('{0}{1}{1}'.format(BNFM, NOT_HAS_FEATURE)))
def test_normalised_form(self): feature1 = SuprasegmentalFeature(self.bfm, 'anterior') character = SuprasegmentalCharacter(self.bfm, 'n') character.set_feature_value(feature1, HAS_FEATURE) self.assertEqual(character.normalised_form, NormalisedForm('{0}{1}'.format(SNFM, HAS_FEATURE))) # Adding a feature changes the normalised form. feature2 = SuprasegmentalFeature(self.bfm, 'dental') # All features must have a value. self.assertRaises(InvalidCharacterError, getattr, character, 'normalised_form') character.set_feature_value(feature2, NOT_HAS_FEATURE) self.assertEqual( character.normalised_form, NormalisedForm('{0}{1}{2}'.format(SNFM, HAS_FEATURE, NOT_HAS_FEATURE))) # The order of the normalised form feature values is # alphabetical by feature name. feature3 = SuprasegmentalFeature(self.bfm, 'consonantal') character.set_feature_value(feature3, HAS_FEATURE) self.assertEqual( character.normalised_form, NormalisedForm('{0}{1}{1}{2}'.format(SNFM, HAS_FEATURE, NOT_HAS_FEATURE))) # Renaming a feature may change the normalised form. feature3.name = 'vocalic' self.assertEqual( character.normalised_form, NormalisedForm('{0}{1}{2}{1}'.format(SNFM, HAS_FEATURE, NOT_HAS_FEATURE))) # Changing a feature value changes the normalised form. character.set_feature_value(feature1, INAPPLICABLE_FEATURE) self.assertEqual( character.normalised_form, NormalisedForm('{0}{3}{2}{1}'.format(SNFM, HAS_FEATURE, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE))) # Removing a feature value changes the normalised form. feature3.delete() self.assertEqual( character.normalised_form, NormalisedForm('{0}{2}{1}'.format(SNFM, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE))) # Adding a feature of a different type does not change the # normalised form. feature4 = BaseFeature(self.bfm, 'syllabic') self.assertEqual( character.normalised_form, NormalisedForm('{0}{2}{1}'.format(SNFM, NOT_HAS_FEATURE, INAPPLICABLE_FEATURE)))
def test_name(self): feature = BaseFeature(self.bfm, 'voiced') self.assertEqual(feature.name, 'voiced') # Feature names can be set. feature.name = 'vocalic' self.assertEqual(feature.name, 'vocalic') # Feature names must be unique across all features in a binary # features model. feature2 = SuprasegmentalFeature(self.bfm, 'consonantal') self.assertRaises(InvalidFeatureError, setattr, feature2, 'name', 'vocalic') # Setting the name to the existing name should not raise an error. feature2.name = 'consonantal' self.assertEqual(feature2.name, 'consonantal')
def test_optional_sections(self): # Optional sections should be optional. configuration = ''' [Base Features] anterior low syllabic voiced [Base Characters] a: b: anterior c: low, voiced''' model = self.parser.parse(configuration) self.assertEqual(len(model.base_features), 4) anterior = BaseFeature(model, 'anterior') low = BaseFeature(model, 'low') syllabic = BaseFeature(model, 'syllabic') voiced = BaseFeature(model, 'voiced') self.assertEqual(len(model.base_features), 4) self.assertTrue(anterior in model.base_features) self.assertTrue(low in model.base_features) self.assertTrue(syllabic in model.base_features) self.assertTrue(voiced in model.base_features) # Base characters. self.assertEqual(len(model.base_characters), 3) a = BaseCharacter(model, 'a') b = BaseCharacter(model, 'b') c = BaseCharacter(model, 'c') self.assertEqual(len(model.base_characters), 3) self.assertTrue(a in model.base_characters) self.assertTrue(b in model.base_characters) self.assertTrue(c in model.base_characters) # Feature values. anterior_has_feature = anterior.get_value_characters(HAS_FEATURE) self.assertEqual(len(anterior_has_feature), 1) self.assertTrue(b in anterior_has_feature) anterior_not_has_feature = anterior.get_value_characters( NOT_HAS_FEATURE) self.assertEqual(len(anterior_not_has_feature), 2) self.assertTrue(a in anterior_not_has_feature) self.assertTrue(c in anterior_not_has_feature) anterior_inapplicable_feature = anterior.get_value_characters( INAPPLICABLE_FEATURE) self.assertEqual(len(anterior_inapplicable_feature), 0)
def test_normalised_form_cluster_creation(self): # Test that the correct subclass of Cluster is created from a # normalised form. bfm = BinaryFeaturesModel() feature1 = BaseFeature(bfm, 'anterior') feature2 = SuprasegmentalFeature(bfm, 'long') character1 = BaseCharacter(bfm, 'a') character1.set_feature_value(feature1, HAS_FEATURE) character2 = SuprasegmentalCharacter(bfm, 'b') character2.set_feature_value(feature2, HAS_FEATURE) nf1 = NormalisedForm('{}{}'.format(BNFM, HAS_FEATURE)) nf2 = NormalisedForm('{}{}'.format(SNFM, HAS_FEATURE)) cluster1 = Cluster(bfm, normalised_form=nf1) cluster2 = Cluster(bfm, normalised_form=nf2) self.assertTrue(isinstance(cluster1, BaseCluster)) self.assertTrue(isinstance(cluster2, SuprasegmentalCluster)) self.assertRaises(IllegalArgumentError, Cluster, '')
def test_applier_form (self): a = BaseCharacter(self.bfm, 'a') cluster1 = BaseCluster(self.bfm, base_character=a) cluster1_form = '{0}{1}{2}{3}{3}'.format(AFM, BNFM, HAS_FEATURE, NOT_HAS_FEATURE) self.optional.append(cluster1) expected = '({})?'.format(cluster1_form) self.assertEqual(self.optional.applier_form, expected) self.optional.match_multiple = True expected = '({})*'.format(cluster1_form) self.assertEqual(self.optional.applier_form, expected) voiced = BaseFeature(self.bfm, 'voiced') feature_set = BaseFeatureSet(self.bfm) feature_set.set(voiced, HAS_FEATURE) feature_set_form = '{0}{1}{2}{2}{3}'.format( AFM, BNFM, INAPPLICABLE_FEATURE, HAS_FEATURE) self.optional.append(feature_set) expected = '({}{})*'.format(cluster1_form, feature_set_form) self.assertEqual(self.optional.applier_form, expected)
def test_object_caching(self): # Creating a Character with the same IPA form as an existing # Character must return the existing Character, if both are # associated with the same BinaryFeaturesModel. character1 = BaseCharacter(self.bfm, 'a') character2 = BaseCharacter(self.bfm, 'a') self.assertEqual(character1, character2) character3 = BaseCharacter(self.bfm, 'b') self.assertNotEqual(character1, character3) bfm2 = BinaryFeaturesModel() character4 = BaseCharacter(bfm2, 'a') self.assertNotEqual(character1, character4) # It is an error to create a Character with the same IPA form # but of a different type (subclass). self.assertRaises(InvalidCharacterError, DiacriticCharacter, self.bfm, 'a') # Initialising of the Character should happen only once. feature = BaseFeature(self.bfm, 'voiced') character1.set_feature_value(feature, HAS_FEATURE) self.assertEqual(character1.get_feature_value(feature), HAS_FEATURE) character5 = BaseCharacter(self.bfm, 'a') self.assertEqual(character1.get_feature_value(feature), HAS_FEATURE) character6 = DiacriticCharacter(self.bfm, 'c') character6.set_feature_value(feature, HAS_FEATURE) self.assertEqual(character6.get_feature_value(feature), HAS_FEATURE) character7 = DiacriticCharacter(self.bfm, 'c') self.assertEqual(character6.get_feature_value(feature), HAS_FEATURE) character8 = SpacingCharacter(self.bfm, 'd') character8.set_feature_value(feature, HAS_FEATURE) self.assertEqual(character8.get_feature_value(feature), HAS_FEATURE) character9 = SpacingCharacter(self.bfm, 'd') self.assertEqual(character8.get_feature_value(feature), HAS_FEATURE) character10 = SuprasegmentalCharacter(self.bfm, 'e') feature2 = SuprasegmentalFeature(self.bfm, 'syllabic') character10.set_feature_value(feature2, HAS_FEATURE) self.assertEqual(character10.get_feature_value(feature2), HAS_FEATURE) character11 = SuprasegmentalCharacter(self.bfm, 'e') self.assertEqual(character10.get_feature_value(feature2), HAS_FEATURE)
def test_model(self): configuration = ''' [Base Features] anterior low syllabic voiced [Base Characters] a: b: anterior c: low, voiced [Diacritic Characters] ̥: +anterior [Spacing Characters] ʰ: -low, +voiced [Suprasegmental Features] phrase stressed [Suprasegmental Characters] |: +phrase ˈ: +stressed, +phrase ''' model = self.parser.parse(configuration) # Base features. self.assertEqual(len(model.base_features), 4) anterior = BaseFeature(model, 'anterior') low = BaseFeature(model, 'low') syllabic = BaseFeature(model, 'syllabic') voiced = BaseFeature(model, 'voiced') self.assertEqual(len(model.base_features), 4) self.assertTrue(anterior in model.base_features) self.assertTrue(low in model.base_features) self.assertTrue(syllabic in model.base_features) self.assertTrue(voiced in model.base_features) # Base characters. self.assertEqual(len(model.base_characters), 3) a = BaseCharacter(model, 'a') b = BaseCharacter(model, 'b') c = BaseCharacter(model, 'c') self.assertEqual(len(model.base_characters), 3) self.assertTrue(a in model.base_characters) self.assertTrue(b in model.base_characters) self.assertTrue(c in model.base_characters) # Diacritic characters. self.assertEqual(len(model.diacritic_characters), 1) ring = DiacriticCharacter(model, '̥') self.assertEqual(len(model.diacritic_characters), 1) self.assertTrue(ring in model.diacritic_characters) # Spacing characters. self.assertEqual(len(model.spacing_characters), 1) ʰ = SpacingCharacter(model, 'ʰ') self.assertEqual(len(model.spacing_characters), 1) self.assertTrue(ʰ in model.spacing_characters) # Suprasegmental features. self.assertEqual(len(model.suprasegmental_features), 2) phrase = SuprasegmentalFeature(model, 'phrase') stressed = SuprasegmentalFeature(model, 'stressed') self.assertEqual(len(model.suprasegmental_features), 2) self.assertTrue(phrase in model.suprasegmental_features) self.assertTrue(stressed in model.suprasegmental_features) # Suprasegmental characters. self.assertEqual(len(model.suprasegmental_characters), 2) bar = SuprasegmentalCharacter(model, '|') ˈ = SuprasegmentalCharacter(model, 'ˈ') self.assertEqual(len(model.suprasegmental_characters), 2) self.assertTrue(bar in model.suprasegmental_characters) self.assertTrue(ˈ in model.suprasegmental_characters) # Feature values. anterior_has_feature = anterior.get_value_characters(HAS_FEATURE) self.assertEqual(len(anterior_has_feature), 2) self.assertTrue(b in anterior_has_feature) self.assertTrue(ring in anterior_has_feature) anterior_not_has_feature = anterior.get_value_characters( NOT_HAS_FEATURE) self.assertEqual(len(anterior_not_has_feature), 2) self.assertTrue(a in anterior_not_has_feature) self.assertTrue(c in anterior_not_has_feature) anterior_inapplicable_feature = anterior.get_value_characters( INAPPLICABLE_FEATURE) self.assertEqual(len(anterior_inapplicable_feature), 1) self.assertTrue(ʰ in anterior_inapplicable_feature) phrase_has_feature = phrase.get_value_characters(HAS_FEATURE) self.assertEqual(len(phrase_has_feature), 2) self.assertTrue(bar in phrase_has_feature) self.assertTrue(ˈ in phrase_has_feature) phrase_not_has_feature = phrase.get_value_characters(NOT_HAS_FEATURE) self.assertEqual(len(phrase_not_has_feature), 0) phrase_inapplicable_feature = phrase.get_value_characters( INAPPLICABLE_FEATURE) self.assertEqual(len(phrase_inapplicable_feature), 0)