diff --git a/README.md b/README.md index 46b6972c..3c4b5578 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,7 @@ Luxembourg | LU | 352 | | | Yes Malta | MT | 356 | | Yes | Yes | Yes | | Yes | Yes | Yes | | Yes Moldova | MD | 373 | 0 | Yes | Yes | | | Yes | Yes | Yes | Yes | Monaco | MC | 377 | | | Yes | | | Yes | | | | Yes +Montenegro | ME | 382 | 0 | Yes | Yes | | | Yes | Yes | Yes | Yes | Yes Netherlands | NL | 31 | 0 | Yes | Yes | Yes | | Yes | Yes | Yes | | Yes Norway | NO | 47 | | | Yes | | | Yes | | | | Yes Poland | PL | 48 | | Yes | Yes | Yes | | Yes | | | | diff --git a/src/PhoneNumbers/CountryInfo_Europe.cs b/src/PhoneNumbers/CountryInfo_Europe.cs index 21f01d3f..e6eb2eaf 100644 --- a/src/PhoneNumbers/CountryInfo_Europe.cs +++ b/src/PhoneNumbers/CountryInfo_Europe.cs @@ -418,6 +418,20 @@ public partial class CountryInfo NsnLengths = new ReadOnlyCollection([8, 9, 12]), }; + /// + /// Gets the for Montenegro. + /// + public static CountryInfo Montenegro { get; } = new() + { + CallingCode = "382", + Continent = Europe, + Iso3166Code = "ME", + Name = "Montenegro", + NdcLengths = new ReadOnlyCollection([2]), + NsnLengths = new ReadOnlyCollection([4, 5, 8, 12]), + TrunkPrefix = "0", + }; + /// /// Gets the for Netherlands. /// diff --git a/src/PhoneNumbers/DataFiles/ME.txt b/src/PhoneNumbers/DataFiles/ME.txt new file mode 100644 index 00000000..8964fa2e --- /dev/null +++ b/src/PhoneNumbers/DataFiles/ME.txt @@ -0,0 +1,20 @@ +# References: +# - https://www.itu.int/dms_pub/itu-t/oth/02/02/T02020000DA0002PDFE.pdf +# - https://www.ekip.me/media/documents/general/1656937726_Plan%20numeracije%20CG.pdf +G|20|Podgorica, Tuzi, Danilovgrad, Kolašin|000000-999999| +G|30|Bar, Ulcinj|000000-999999| +G|31|Herceg Novi|000000-999999| +G|32|Kotor, Tivat|000000-999999| +G|33|Budva|000000-999999| +G|40|Nikšić, Šavnik, Plužine|000000-999999| +G|41|Cetinje|000000-999999| +G|50|Bijelo Polje, Mojkovac|000000-999999| +G|51|Berane, Andrijevica, Rožaje, Plav, Petnjica, Gusinje|000000-999999| +G|52|Pljevlja, Žabljak|000000-999999| +M|60,68||00-9999999999| +M|63,66-67,69||00-9999999999| +N|70,77-78||000000-999999| +N|71||0000000000-9999999999|M +N|80||000000-999999|F +N|83||000000-999999|S +N|94-95||000000-999999|R diff --git a/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_GeographicNumber.cs b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_GeographicNumber.cs new file mode 100644 index 00000000..d23f587d --- /dev/null +++ b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_GeographicNumber.cs @@ -0,0 +1,101 @@ +namespace PhoneNumbers.Tests.Parsers; + +/// +/// Contains unit tests for the class for Montenegro s. +/// +public class DefaultPhoneNumberParserTests_ME_GeographicNumber +{ + private static readonly PhoneNumberParser s_parser = DefaultPhoneNumberParser.Create(CountryInfo.Montenegro); + + [Theory] + [InlineData("020000000", "20", "000000", "Podgorica, Tuzi, Danilovgrad, Kolašin")] + [InlineData("020999999", "20", "999999", "Podgorica, Tuzi, Danilovgrad, Kolašin")] + public void Parse_Known_GeographicPhoneNumber_2X_NationalDestinationCode(string value, string NationalDestinationCode, string subscriberNumber, string geographicArea) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var geographicPhoneNumber = (GeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, geographicPhoneNumber.Country); + Assert.Equal(geographicArea, geographicPhoneNumber.GeographicArea); + Assert.Equal(NationalDestinationCode, geographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, geographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("030000000", "30", "000000", "Bar, Ulcinj")] + [InlineData("030999999", "30", "999999", "Bar, Ulcinj")] + [InlineData("031000000", "31", "000000", "Herceg Novi")] + [InlineData("031999999", "31", "999999", "Herceg Novi")] + [InlineData("032000000", "32", "000000", "Kotor, Tivat")] + [InlineData("032999999", "32", "999999", "Kotor, Tivat")] + [InlineData("033000000", "33", "000000", "Budva")] + [InlineData("033999999", "33", "999999", "Budva")] + public void Parse_Known_GeographicPhoneNumber_3X_NationalDestinationCode(string value, string NationalDestinationCode, string subscriberNumber, string geographicArea) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var geographicPhoneNumber = (GeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, geographicPhoneNumber.Country); + Assert.Equal(geographicArea, geographicPhoneNumber.GeographicArea); + Assert.Equal(NationalDestinationCode, geographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, geographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("040000000", "40", "000000", "Nikšić, Šavnik, Plužine")] + [InlineData("040999999", "40", "999999", "Nikšić, Šavnik, Plužine")] + [InlineData("041000000", "41", "000000", "Cetinje")] + [InlineData("041999999", "41", "999999", "Cetinje")] + public void Parse_Known_GeographicPhoneNumber_4X_NationalDestinationCode(string value, string NationalDestinationCode, string subscriberNumber, string geographicArea) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var geographicPhoneNumber = (GeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, geographicPhoneNumber.Country); + Assert.Equal(geographicArea, geographicPhoneNumber.GeographicArea); + Assert.Equal(NationalDestinationCode, geographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, geographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("050000000", "50", "000000", "Bijelo Polje, Mojkovac")] + [InlineData("050999999", "50", "999999", "Bijelo Polje, Mojkovac")] + [InlineData("051000000", "51", "000000", "Berane, Andrijevica, Rožaje, Plav, Petnjica, Gusinje")] + [InlineData("051999999", "51", "999999", "Berane, Andrijevica, Rožaje, Plav, Petnjica, Gusinje")] + [InlineData("052000000", "52", "000000", "Pljevlja, Žabljak")] + [InlineData("052999999", "52", "999999", "Pljevlja, Žabljak")] + public void Parse_Known_GeographicPhoneNumber_5X_NationalDestinationCode(string value, string NationalDestinationCode, string subscriberNumber, string geographicArea) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var geographicPhoneNumber = (GeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, geographicPhoneNumber.Country); + Assert.Equal(geographicArea, geographicPhoneNumber.GeographicArea); + Assert.Equal(NationalDestinationCode, geographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, geographicPhoneNumber.SubscriberNumber); + } +} diff --git a/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_MobilePhoneNumber.cs b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_MobilePhoneNumber.cs new file mode 100644 index 00000000..c8bd3879 --- /dev/null +++ b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_MobilePhoneNumber.cs @@ -0,0 +1,40 @@ +namespace PhoneNumbers.Tests.Parsers; + +/// +/// Contains unit tests for the class for Montenegro s. +/// +public class DefaultPhoneNumberParserTests_ME_MobilePhoneNumber +{ + private static readonly PhoneNumberParser s_parser = DefaultPhoneNumberParser.Create(CountryInfo.Montenegro); + + [Theory] + [InlineData("06000", "60", "00")] + [InlineData("0609999999999", "60", "9999999999")] + [InlineData("06300", "63", "00")] + [InlineData("0639999999999", "63", "9999999999")] + [InlineData("06600", "66", "00")] + [InlineData("0669999999999", "66", "9999999999")] + [InlineData("06700", "67", "00")] + [InlineData("0679999999999", "67", "9999999999")] + [InlineData("06800", "68", "00")] + [InlineData("0689999999999", "68", "9999999999")] + [InlineData("06900", "69", "00")] + [InlineData("0699999999999", "69", "9999999999")] + public void Parse_Known_MobilePhoneNumber(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var mobilePhoneNumber = (MobilePhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, mobilePhoneNumber.Country); + Assert.False(mobilePhoneNumber.IsPager); + Assert.False(mobilePhoneNumber.IsVirtual); + Assert.Equal(NationalDestinationCode, mobilePhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, mobilePhoneNumber.SubscriberNumber); + } +} diff --git a/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_NonGeographicPhoneNumber.cs b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_NonGeographicPhoneNumber.cs new file mode 100644 index 00000000..04c8daec --- /dev/null +++ b/test/PhoneNumbers.Data.Tests/Parsers/DefaultPhoneNumberParserTests_ME_NonGeographicPhoneNumber.cs @@ -0,0 +1,130 @@ +namespace PhoneNumbers.Tests.Parsers; + +/// +/// Contains unit tests for the class for Montenegro s. +/// +public class DefaultPhoneNumberParserTests_ME_NonGeographicPhoneNumber +{ + private static readonly PhoneNumberParser s_parser = DefaultPhoneNumberParser.Create(CountryInfo.Montenegro); + + [Theory] + [InlineData("070000000", "70", "000000")] + [InlineData("070999999", "70", "999999")] + [InlineData("077000000", "77", "000000")] + [InlineData("077999999", "77", "999999")] + [InlineData("078000000", "78", "000000")] + [InlineData("078999999", "78", "999999")] + public void Parse_Known_NonGeographicPhoneNumber_7X_NationalDestinationCode(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var nonGeographicPhoneNumber = (NonGeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, nonGeographicPhoneNumber.Country); + Assert.False(nonGeographicPhoneNumber.IsFreephone); + Assert.False(nonGeographicPhoneNumber.IsMachineToMachine); + Assert.False(nonGeographicPhoneNumber.IsPremiumRate); + Assert.False(nonGeographicPhoneNumber.IsSharedCost); + Assert.Equal(NationalDestinationCode, nonGeographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, nonGeographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("080000000", "80", "000000")] + [InlineData("080999999", "80", "999999")] + public void Parse_Known_NonGeographicPhoneNumber_Freephone(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var nonGeographicPhoneNumber = (NonGeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, nonGeographicPhoneNumber.Country); + Assert.True(nonGeographicPhoneNumber.IsFreephone); + Assert.False(nonGeographicPhoneNumber.IsMachineToMachine); + Assert.False(nonGeographicPhoneNumber.IsPremiumRate); + Assert.False(nonGeographicPhoneNumber.IsSharedCost); + Assert.Equal(NationalDestinationCode, nonGeographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, nonGeographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("0710000000000", "71", "0000000000")] + [InlineData("0719999999999", "71", "9999999999")] + public void Parse_Known_NonGeographicPhoneNumber_MachineToMachine(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var nonGeographicPhoneNumber = (NonGeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, nonGeographicPhoneNumber.Country); + Assert.False(nonGeographicPhoneNumber.IsFreephone); + Assert.True(nonGeographicPhoneNumber.IsMachineToMachine); + Assert.False(nonGeographicPhoneNumber.IsPremiumRate); + Assert.False(nonGeographicPhoneNumber.IsSharedCost); + Assert.Equal(NationalDestinationCode, nonGeographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, nonGeographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("094000000", "94", "000000")] + [InlineData("094999999", "94", "999999")] + [InlineData("095000000", "95", "000000")] + [InlineData("095999999", "95", "999999")] + public void Parse_Known_NonGeographicPhoneNumber_PremiumRate(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var nonGeographicPhoneNumber = (NonGeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, nonGeographicPhoneNumber.Country); + Assert.False(nonGeographicPhoneNumber.IsFreephone); + Assert.False(nonGeographicPhoneNumber.IsMachineToMachine); + Assert.True(nonGeographicPhoneNumber.IsPremiumRate); + Assert.False(nonGeographicPhoneNumber.IsSharedCost); + Assert.Equal(NationalDestinationCode, nonGeographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, nonGeographicPhoneNumber.SubscriberNumber); + } + + [Theory] + [InlineData("083000000", "83", "000000")] + [InlineData("083999999", "83", "999999")] + public void Parse_Known_NonGeographicPhoneNumber_SharedCost(string value, string NationalDestinationCode, string subscriberNumber) + { + var parseResult = s_parser.Parse(value); + parseResult.ThrowIfFailure(); + + var phoneNumber = parseResult.PhoneNumber; + + Assert.NotNull(phoneNumber); + Assert.IsType(phoneNumber); + + var nonGeographicPhoneNumber = (NonGeographicPhoneNumber)phoneNumber; + Assert.Equal(CountryInfo.Montenegro, nonGeographicPhoneNumber.Country); + Assert.False(nonGeographicPhoneNumber.IsFreephone); + Assert.False(nonGeographicPhoneNumber.IsMachineToMachine); + Assert.False(nonGeographicPhoneNumber.IsPremiumRate); + Assert.True(nonGeographicPhoneNumber.IsSharedCost); + Assert.Equal(NationalDestinationCode, nonGeographicPhoneNumber.NationalDestinationCode); + Assert.Equal(subscriberNumber, nonGeographicPhoneNumber.SubscriberNumber); + } +} diff --git a/test/PhoneNumbers.Tests/CountryInfo_Europe_Tests.cs b/test/PhoneNumbers.Tests/CountryInfo_Europe_Tests.cs index f027ba40..bde35dfa 100644 --- a/test/PhoneNumbers.Tests/CountryInfo_Europe_Tests.cs +++ b/test/PhoneNumbers.Tests/CountryInfo_Europe_Tests.cs @@ -544,6 +544,26 @@ public void CountryInfo_Monaco() Assert.Null(countryInfo.TrunkPrefix); } + [Fact] + public void CountryInfo_Montenegro() + { + Assert.Same(CountryInfo.Montenegro, CountryInfo.Montenegro); + + var countryInfo = CountryInfo.Montenegro; + + Assert.False(countryInfo.AllowsLocalGeographicDialling); + Assert.Equal("382", countryInfo.CallingCode); + Assert.Equal(CountryInfo.Europe, countryInfo.Continent); + Assert.IsType(countryInfo.FormatProvider); + Assert.False(countryInfo.IsEuropeanUnionMember); + Assert.Equal("ME", countryInfo.Iso3166Code); + Assert.Equal("Montenegro", countryInfo.Name); + Assert.Equal(new[] { 2 }, countryInfo.NdcLengths); + Assert.Equal(new[] { 4, 5, 8, 12 }, countryInfo.NsnLengths); + Assert.False(countryInfo.SharesCallingCode); + Assert.Equal("0", countryInfo.TrunkPrefix); + } + [Fact] public void CountryInfo_Netherlands() { diff --git a/test/PhoneNumbers.Tests/PhoneNumber_Parse_Europe_Tests.cs b/test/PhoneNumbers.Tests/PhoneNumber_Parse_Europe_Tests.cs index 57e555b6..32d6a4dd 100644 --- a/test/PhoneNumbers.Tests/PhoneNumber_Parse_Europe_Tests.cs +++ b/test/PhoneNumbers.Tests/PhoneNumber_Parse_Europe_Tests.cs @@ -218,6 +218,14 @@ public void Parse_Value_For_Monaco_CallingCode() Assert.Equal(CountryInfo.Monaco, phoneNumber.Country); } + [Fact] + public void Parse_Value_For_Montenegro_CallingCode() + { + var phoneNumber = PhoneNumber.Parse("+38220406700"); + Assert.NotNull(phoneNumber); + Assert.Equal(CountryInfo.Montenegro, phoneNumber.Country); + } + [Fact] public void Parse_Value_For_Netherlands_CallingCode() { diff --git a/test/PhoneNumbers.Tests/PhoneNumber_ToString_Europe_Tests.cs b/test/PhoneNumbers.Tests/PhoneNumber_ToString_Europe_Tests.cs index f3e135cd..98af8265 100644 --- a/test/PhoneNumbers.Tests/PhoneNumber_ToString_Europe_Tests.cs +++ b/test/PhoneNumbers.Tests/PhoneNumber_ToString_Europe_Tests.cs @@ -227,6 +227,13 @@ public void Moldova_Numbers(string input, string format, string expected) => public void Monaco_Numbers(string input, string format, string expected) => Assert.Equal(expected, PhoneNumber.Parse(input).ToString(format)); + [Theory] + [InlineData("+38220406700", "E.123", "+382 20 406 700")] + [InlineData("+38220406700", "N", "020 406 700")] + [InlineData("+38220406700", "RFC3966", "tel:+382-20-406-700")] + public void Montenegro_Numbers(string input, string format, string expected) => + Assert.Equal(expected, PhoneNumber.Parse(input).ToString(format)); + [Theory] [InlineData("+31702140214", "E.123", "+31 7 02 14 02 14")] [InlineData("+31702140214", "N", "07 02 14 02 14")] diff --git a/utilities/phone-number-utilities-testgenerator b/utilities/phone-number-utilities-testgenerator index faaf0e29..73503a77 160000 --- a/utilities/phone-number-utilities-testgenerator +++ b/utilities/phone-number-utilities-testgenerator @@ -1 +1 @@ -Subproject commit faaf0e29b2efede538c8e26c06582f6b5f543ea3 +Subproject commit 73503a7728faafd0f64b4cb35aad106de060aeca