diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 6f4a50c75..feec96bd0 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -189,6 +189,7 @@ set ( "src/phonenumbers/phonenumber.pb.cc" # Generated by Protocol Buffers. "src/phonenumbers/phonenumberutil.cc" "src/phonenumbers/regexp_cache.cc" + "src/phonenumbers/shortnumberutil.cc" "src/phonenumbers/string_byte_sink.cc" "src/phonenumbers/stringutil.cc" "src/phonenumbers/unicodestring.cc" @@ -357,6 +358,7 @@ set (TEST_SOURCES "test/phonenumbers/regexp_adapter_test.cc" "test/phonenumbers/regexp_cache_test.cc" "test/phonenumbers/run_tests.cc" + "test/phonenumbers/shortnumberutil_test.cc" "test/phonenumbers/stringutil_test.cc" "test/phonenumbers/test_util.cc" "test/phonenumbers/unicodestring_test.cc" @@ -395,6 +397,7 @@ install (FILES "src/phonenumbers/phonenumberutil.h" "src/phonenumbers/regexp_adapter.h" "src/phonenumbers/regexp_cache.h" + "src/phonenumbers/shortnumberutil.h" "src/phonenumbers/unicodestring.h" DESTINATION include/phonenumbers/ ) diff --git a/cpp/src/phonenumbers/lite_metadata.cc b/cpp/src/phonenumbers/lite_metadata.cc index 8b59dbf6a..fa95f4c73 100644 --- a/cpp/src/phonenumbers/lite_metadata.cc +++ b/cpp/src/phonenumbers/lite_metadata.cc @@ -3189,7 +3189,7 @@ static const unsigned char data[] = { 0x7B, 0x37, 0x7D, 0x7C, 0x31, 0x28, 0x3F, 0x3A, 0x28, 0x3F, 0x3A, 0x31, 0x28, 0x3F, 0x3A, 0x33, 0x5B, 0x30, 0x2D, 0x34, 0x38, 0x5D, 0x7C, 0x5B, 0x34, 0x36, 0x5D, 0x5B, 0x30, 0x2D, 0x34, 0x5D, 0x7C, 0x35, 0x5B, 0x30, 0x31, 0x32, 0x37, - 0x38, 0x39, 0x5D, 0x7C, 0x37, 0x5B, 0x30, 0x2D, 0x33, 0x39, 0x5D, 0x7C, 0x38, + 0x38, 0x39, 0x5D, 0x7C, 0x37, 0x5B, 0x30, 0x2D, 0x34, 0x39, 0x5D, 0x7C, 0x38, 0x5B, 0x30, 0x31, 0x33, 0x34, 0x39, 0x5D, 0x29, 0x7C, 0x32, 0x31, 0x5B, 0x30, 0x2D, 0x37, 0x5D, 0x7C, 0x33, 0x31, 0x5B, 0x30, 0x2D, 0x38, 0x5D, 0x7C, 0x5B, 0x34, 0x35, 0x39, 0x5D, 0x31, 0x5C, 0x64, 0x7C, 0x36, 0x31, 0x5B, 0x30, 0x2D, diff --git a/cpp/src/phonenumbers/metadata.cc b/cpp/src/phonenumbers/metadata.cc index 0bde65cd2..1ee00d388 100644 --- a/cpp/src/phonenumbers/metadata.cc +++ b/cpp/src/phonenumbers/metadata.cc @@ -3483,7 +3483,7 @@ static const unsigned char data[] = { 0x28, 0x3F, 0x3A, 0x31, 0x28, 0x3F, 0x3A, 0x33, 0x5B, 0x30, 0x2D, 0x34, 0x38, 0x5D, 0x7C, 0x5B, 0x34, 0x36, 0x5D, 0x5B, 0x30, 0x2D, 0x34, 0x5D, 0x7C, 0x35, 0x5B, 0x30, 0x31, 0x32, 0x37, 0x38, 0x39, 0x5D, 0x7C, 0x37, 0x5B, 0x30, 0x2D, - 0x33, 0x39, 0x5D, 0x7C, 0x38, 0x5B, 0x30, 0x31, 0x33, 0x34, 0x39, 0x5D, 0x29, + 0x34, 0x39, 0x5D, 0x7C, 0x38, 0x5B, 0x30, 0x31, 0x33, 0x34, 0x39, 0x5D, 0x29, 0x7C, 0x32, 0x31, 0x5B, 0x30, 0x2D, 0x37, 0x5D, 0x7C, 0x33, 0x31, 0x5B, 0x30, 0x2D, 0x38, 0x5D, 0x7C, 0x5B, 0x34, 0x35, 0x39, 0x5D, 0x31, 0x5C, 0x64, 0x7C, 0x36, 0x31, 0x5B, 0x30, 0x2D, 0x34, 0x36, 0x2D, 0x39, 0x5D, 0x29, 0x29, 0x5C, diff --git a/cpp/src/phonenumbers/phonenumbermatcher.h b/cpp/src/phonenumbers/phonenumbermatcher.h index 3917f551c..efa56734d 100644 --- a/cpp/src/phonenumbers/phonenumbermatcher.h +++ b/cpp/src/phonenumbers/phonenumbermatcher.h @@ -33,6 +33,9 @@ namespace i18n { namespace phonenumbers { +template + class ResultCallback4; + using std::string; using std::vector; diff --git a/cpp/src/phonenumbers/phonenumberutil.cc b/cpp/src/phonenumbers/phonenumberutil.cc index 7946b0422..2497f2ddc 100644 --- a/cpp/src/phonenumbers/phonenumberutil.cc +++ b/cpp/src/phonenumbers/phonenumberutil.cc @@ -17,9 +17,9 @@ #include "phonenumbers/phonenumberutil.h" +#include #include #include -#include #include #include #include @@ -73,10 +73,10 @@ const char PhoneNumberUtil::kPlusChars[] = "+\xEF\xBC\x8B"; /* "++" */ // unicode character. // static const char PhoneNumberUtil::kValidPunctuation[] = - /* "-x‐-―−ー--/   ()()[].\\[\\]/~⁓∼" */ + /* "-x‐-―−ー--/  ­ ()()[].\\[\\]/~⁓∼" */ "-x\xE2\x80\x90-\xE2\x80\x95\xE2\x88\x92\xE3\x83\xBC\xEF\xBC\x8D-\xEF\xBC" - "\x8F \xC2\xA0\xE2\x80\x8B\xE2\x81\xA0\xE3\x80\x80()\xEF\xBC\x88\xEF\xBC" - "\x89\xEF\xBC\xBB\xEF\xBC\xBD.\\[\\]/~\xE2\x81\x93\xE2\x88\xBC"; + "\x8F \xC2\xA0\xC2\xAD\xE2\x80\x8B\xE2\x81\xA0\xE3\x80\x80()\xEF\xBC\x88" + "\xEF\xBC\x89\xEF\xBC\xBB\xEF\xBC\xBD.\\[\\]/~\xE2\x81\x93\xE2\x88\xBC"; // static const char PhoneNumberUtil::kCaptureUpToSecondNumberStart[] = "(.*)[\\\\/] *x"; @@ -97,9 +97,8 @@ const char kStarSign[] = "*"; const char kRfc3966ExtnPrefix[] = ";ext="; const char kRfc3966Prefix[] = "tel:"; -// We include the "+" here since RFC3966 format specifies that the context must -// be specified in international format. -const char kRfc3966PhoneContext[] = ";phone-context=+"; +const char kRfc3966PhoneContext[] = ";phone-context="; +const char kRfc3966IsdnSubaddress[] = ";isub="; const char kDigits[] = "\\p{Nd}"; // We accept alpha characters in phone numbers, ASCII only. We store lower-case @@ -588,8 +587,8 @@ class PhoneNumberRegExpsAndMappings { kStarSign)), valid_phone_number_( StrCat("[", PhoneNumberUtil::kPlusChars, "]*(?:[", - punctuation_and_star_sign_, "]*[", - kDigits, "]){3,}[", kValidAlpha, + punctuation_and_star_sign_, "]*", + kDigits, "){3,}[", kValidAlpha, punctuation_and_star_sign_, kDigits, "]*")), extn_patterns_for_parsing_( @@ -740,6 +739,13 @@ const string& PhoneNumberUtil::GetExtnPatternsForMatching() const { return reg_exps_->extn_patterns_for_matching_; } +bool PhoneNumberUtil::StartsWithPlusCharsPattern(const string& number) + const { + const scoped_ptr number_string_piece( + reg_exps_->regexp_factory_->CreateInput(number)); + return reg_exps_->plus_chars_pattern_->Consume(number_string_piece.get()); +} + bool PhoneNumberUtil::ContainsOnlyValidDigits(const string& s) const { return reg_exps_->digits_pattern_->FullMatch(s); } @@ -1202,9 +1208,11 @@ void PhoneNumberUtil::FormatInOriginalFormat(const PhoneNumber& number, // user entered. if (!formatted_number->empty()) { string formatted_number_copy(*formatted_number); - NormalizeDigitsOnly(&formatted_number_copy); + NormalizeHelper(reg_exps_->diallable_char_mappings_, + true /* remove non matches */, &formatted_number_copy); string raw_input_copy(number.raw_input()); - NormalizeDigitsOnly(&raw_input_copy); + NormalizeHelper(reg_exps_->diallable_char_mappings_, + true /* remove non matches */, &raw_input_copy); if (formatted_number_copy != raw_input_copy) { formatted_number->assign(number.raw_input()); } @@ -1698,38 +1706,70 @@ bool PhoneNumberUtil::CheckRegionForParsing( return true; } -PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper( - const string& number_to_parse, - const string& default_region, - bool keep_raw_input, - bool check_region, - PhoneNumber* phone_number) const { - DCHECK(phone_number); - +// Converts number_to_parse to a form that we can parse and write it to +// national_number if it is written in RFC3966; otherwise extract a possible +// number out of it and write to national_number. +void PhoneNumberUtil::BuildNationalNumberForParsing( + const string& number_to_parse, string* national_number) const { size_t index_of_phone_context = number_to_parse.find(kRfc3966PhoneContext); - string national_number; if (index_of_phone_context != string::npos) { - // Prefix the number with the phone context. The offset here is because the - // context we are expecting to match should start with a "+" sign, and we - // want to include this at the start of the number. - StrAppend( - &national_number, - number_to_parse.substr( - index_of_phone_context + strlen(kRfc3966PhoneContext) - 1)); + int phone_context_start = + index_of_phone_context + strlen(kRfc3966PhoneContext); + // If the phone context contains a phone number prefix, we need to capture + // it, whereas domains will be ignored. + if (number_to_parse.at(phone_context_start) == kPlusSign[0]) { + // Additional parameters might follow the phone context. If so, we will + // remove them here because the parameters after phone context are not + // important for parsing the phone number. + size_t phone_context_end = number_to_parse.find(';', phone_context_start); + if (phone_context_end != string::npos) { + StrAppend( + national_number, number_to_parse.substr( + phone_context_start, phone_context_end - phone_context_start)); + } else { + StrAppend(national_number, number_to_parse.substr(phone_context_start)); + } + } + // Now append everything between the "tel:" prefix and the phone-context. + // This should include the national number, an optional extension or + // isdn-subaddress component. int end_of_rfc_prefix = number_to_parse.find(kRfc3966Prefix) + strlen(kRfc3966Prefix); StrAppend( - &national_number, + national_number, number_to_parse.substr(end_of_rfc_prefix, index_of_phone_context - end_of_rfc_prefix)); - // Note that phone-contexts that are URLs will not be parsed - - // IsViablePhoneNumber will throw an exception below. } else { // Extract a possible number from the string passed in (this strips leading // characters that could not be the start of a phone number.) - ExtractPossibleNumber(number_to_parse, &national_number); + ExtractPossibleNumber(number_to_parse, national_number); + } + + // Delete the isdn-subaddress and everything after it if it is present. Note + // extension won't appear at the same time with isdn-subaddress according to + // paragraph 5.3 of the RFC3966 spec. + size_t index_of_isdn = national_number->find(kRfc3966IsdnSubaddress); + if (index_of_isdn != string::npos) { + national_number->erase(index_of_isdn); } + // If both phone context and isdn-subaddress are absent but other parameters + // are present, the parameters are left in nationalNumber. This is because + // we are concerned about deleting content from a potential number string + // when there is no strong evidence that the number is actually written in + // RFC3966. +} + +PhoneNumberUtil::ErrorType PhoneNumberUtil::ParseHelper( + const string& number_to_parse, + const string& default_region, + bool keep_raw_input, + bool check_region, + PhoneNumber* phone_number) const { + DCHECK(phone_number); + + string national_number; + BuildNationalNumberForParsing(number_to_parse, &national_number); if (!IsViablePhoneNumber(national_number)) { VLOG(2) << "The string supplied did not seem to be a phone number."; @@ -2390,7 +2430,7 @@ PhoneNumberUtil::ErrorType PhoneNumberUtil::MaybeExtractCountryCode( phone_number->set_country_code_source(country_code_source); } if (country_code_source != PhoneNumber::FROM_DEFAULT_COUNTRY) { - if (national_number->length() < kMinLengthForNsn) { + if (national_number->length() <= kMinLengthForNsn) { VLOG(2) << "Phone number had an IDD, but after this was not " << "long enough to be a viable phone number."; return TOO_SHORT_AFTER_IDD; diff --git a/cpp/src/phonenumbers/phonenumberutil.h b/cpp/src/phonenumbers/phonenumberutil.h index 3aeb1fb71..3013b3f63 100644 --- a/cpp/src/phonenumbers/phonenumberutil.h +++ b/cpp/src/phonenumbers/phonenumberutil.h @@ -73,6 +73,8 @@ class PhoneNumberUtil : public Singleton { friend class PhoneNumberMatcherTest; friend class PhoneNumberRegExpsAndMappings; friend class PhoneNumberUtilTest; + friend class ShortNumberUtil; + friend class ShortNumberUtilTest; public: ~PhoneNumberUtil(); static const char kRegionCodeForNonGeoEntity[]; @@ -487,6 +489,8 @@ class PhoneNumberUtil : public Singleton { // particular region is not performed. This can be done separately with // IsValidNumber(). // + // number_to_parse can also be provided in RFC3966 format. + // // default_region represents the country that we are expecting the number to // be from. This is only used if the number being parsed is not written in // international format. The country_code for the number in this case would be @@ -565,7 +569,7 @@ class PhoneNumberUtil : public Singleton { typedef pair*> IntRegionsPair; // The minimum and maximum length of the national significant number. - static const size_t kMinLengthForNsn = 3; + static const size_t kMinLengthForNsn = 2; // The ITU says the maximum length should be 15, but we have found longer // numbers in Germany. static const size_t kMaxLengthForNsn = 16; @@ -622,6 +626,9 @@ class PhoneNumberUtil : public Singleton { // in a number, for use when matching. const string& GetExtnPatternsForMatching() const; + // Checks if a number matches the plus chars pattern. + bool StartsWithPlusCharsPattern(const string& number) const; + // Checks whether a string contains only valid digits. bool ContainsOnlyValidDigits(const string& s) const; @@ -748,6 +755,9 @@ class PhoneNumberUtil : public Singleton { bool check_region, PhoneNumber* phone_number) const; + void BuildNationalNumberForParsing(const string& number_to_parse, + string* national_number) const; + // Returns true if the number can be dialled from outside the region, or // unknown. If the number can only be dialled from within the region, returns // false. Does not check the number is a valid number. diff --git a/cpp/src/phonenumbers/shortnumberutil.cc b/cpp/src/phonenumbers/shortnumberutil.cc new file mode 100644 index 000000000..9485d5a8a --- /dev/null +++ b/cpp/src/phonenumbers/shortnumberutil.cc @@ -0,0 +1,78 @@ +// Copyright (C) 2012 The Libphonenumber Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: David Yonge-Mallo + +#include "phonenumbers/shortnumberutil.h" + +#include "base/memory/scoped_ptr.h" +#include "phonenumbers/phonemetadata.pb.h" +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/regexp_adapter.h" +#include "phonenumbers/regexp_factory.h" + +namespace i18n { +namespace phonenumbers { + +using std::string; + +ShortNumberUtil::ShortNumberUtil() + : phone_util_(*PhoneNumberUtil::GetInstance()) { +} + +bool ShortNumberUtil::ConnectsToEmergencyNumber(const string& number, + const string& region_code) const { + return MatchesEmergencyNumberHelper(number, region_code, + true /* allows prefix match */); +} + +bool ShortNumberUtil::IsEmergencyNumber(const string& number, + const string& region_code) const { + return MatchesEmergencyNumberHelper(number, region_code, + false /* doesn't allow prefix match */); +} + +bool ShortNumberUtil::MatchesEmergencyNumberHelper(const string& number, + const string& region_code, bool allow_prefix_match) const { + string extracted_number; + phone_util_.ExtractPossibleNumber(number, &extracted_number); + if (phone_util_.StartsWithPlusCharsPattern(extracted_number)) { + // Returns false if the number starts with a plus sign. We don't believe + // dialing the country code before emergency numbers (e.g. +1911) works, + // but later, if that proves to work, we can add additional logic here to + // handle it. + return false; + } + const PhoneMetadata* metadata = phone_util_.GetMetadataForRegion(region_code); + if (!metadata || !metadata->has_emergency()) { + return false; + } + const scoped_ptr regexp_factory( + new RegExpFactory()); + const scoped_ptr emergency_number_pattern( + regexp_factory->CreateRegExp( + metadata->emergency().national_number_pattern())); + phone_util_.NormalizeDigitsOnly(&extracted_number); + const scoped_ptr normalized_number_input( + regexp_factory->CreateInput(extracted_number)); + + // In Brazil, it is impossible to append additional digits to an emergency + // number to dial the number. + return (!allow_prefix_match || region_code == "BR") + ? emergency_number_pattern->FullMatch(extracted_number) + : emergency_number_pattern->Consume(normalized_number_input.get()); +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/src/phonenumbers/shortnumberutil.h b/cpp/src/phonenumbers/shortnumberutil.h new file mode 100644 index 000000000..53758a157 --- /dev/null +++ b/cpp/src/phonenumbers/shortnumberutil.h @@ -0,0 +1,73 @@ +// Copyright (C) 2012 The Libphonenumber Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Utility for international short phone numbers, such as short codes and +// emergency numbers. Note most commercial short numbers are not handled here, +// but by the phonenumberutil. +// +// Author: David Yonge-Mallo +// +// This is a direct port from ShortNumberUtil.java. +// Changes to this class should also happen to the Java version, whenever it +// makes sense. + +#ifndef I18N_PHONENUMBERS_SHORTNUMBERUTIL_H_ +#define I18N_PHONENUMBERS_SHORTNUMBERUTIL_H_ + +#include + +#include "base/basictypes.h" + +namespace i18n { +namespace phonenumbers { + +using std::string; + +class PhoneNumberUtil; + +class ShortNumberUtil { + public: + ShortNumberUtil(); + + // Returns true if the number might be used to connect to an emergency service + // in the given region. + // + // This method takes into account cases where the number might contain + // formatting, or might have additional digits appended (when it is okay to do + // that in the region specified). + bool ConnectsToEmergencyNumber(const string& number, + const string& region_code) const; + + // Returns true if the number exactly matches an emergency service number in + // the given region. + // + // This method takes into account cases where the number might contain + // formatting, but doesn't allow additional digits to be appended. + bool IsEmergencyNumber(const string& number, + const string& region_code) const; + + private: + const PhoneNumberUtil& phone_util_; + + bool MatchesEmergencyNumberHelper(const string& number, + const string& region_code, + bool allow_prefix_match) const; + + DISALLOW_COPY_AND_ASSIGN(ShortNumberUtil); +}; + +} // namespace phonenumbers +} // namespace i18n + +#endif // I18N_PHONENUMBERS_SHORTNUMBERUTIL_H_ diff --git a/cpp/test/phonenumbers/phonenumberutil_test.cc b/cpp/test/phonenumbers/phonenumberutil_test.cc index 4266d26b7..939fa8937 100644 --- a/cpp/test/phonenumbers/phonenumberutil_test.cc +++ b/cpp/test/phonenumbers/phonenumberutil_test.cc @@ -310,6 +310,13 @@ TEST_F(PhoneNumberUtilTest, GetExampleNumberForNonGeoEntity) { phone_util_.GetExampleNumberForNonGeoEntity(800 , &test_number); EXPECT_TRUE(success); EXPECT_EQ(toll_free_number, test_number); + + PhoneNumber universal_premium_rate; + universal_premium_rate.set_country_code(979); + universal_premium_rate.set_national_number(123456789ULL); + success = phone_util_.GetExampleNumberForNonGeoEntity(979 , &test_number); + EXPECT_TRUE(success); + EXPECT_EQ(universal_premium_rate, test_number); } TEST_F(PhoneNumberUtilTest, FormatUSNumber) { @@ -1264,6 +1271,11 @@ TEST_F(PhoneNumberUtilTest, IsValidNumber) { intl_toll_free_number.set_country_code(800); intl_toll_free_number.set_national_number(12345678ULL); EXPECT_TRUE(phone_util_.IsValidNumber(intl_toll_free_number)); + + PhoneNumber universal_premium_rate; + universal_premium_rate.set_country_code(979); + universal_premium_rate.set_national_number(123456789ULL); + EXPECT_TRUE(phone_util_.IsValidNumber(universal_premium_rate)); } TEST_F(PhoneNumberUtilTest, IsValidForRegion) { @@ -1373,6 +1385,53 @@ TEST_F(PhoneNumberUtilTest, IsNotValidNumber) { EXPECT_FALSE(phone_util_.IsValidNumber(intl_toll_free_number_too_long)); } +TEST_F(PhoneNumberUtilTest, GetRegionCodeForCountryCode) { + string region_code; + phone_util_.GetRegionCodeForCountryCode(1, ®ion_code); + EXPECT_EQ(RegionCode::US(), region_code); + phone_util_.GetRegionCodeForCountryCode(44, ®ion_code); + EXPECT_EQ(RegionCode::GB(), region_code); + phone_util_.GetRegionCodeForCountryCode(49, ®ion_code); + EXPECT_EQ(RegionCode::DE(), region_code); + phone_util_.GetRegionCodeForCountryCode(800, ®ion_code); + EXPECT_EQ(RegionCode::UN001(), region_code); + phone_util_.GetRegionCodeForCountryCode(979, ®ion_code); + EXPECT_EQ(RegionCode::UN001(), region_code); +} + +TEST_F(PhoneNumberUtilTest, GetRegionCodeForNumber) { + string region_code; + PhoneNumber bs_number; + bs_number.set_country_code(1); + bs_number.set_national_number(2423232345ULL); + phone_util_.GetRegionCodeForNumber(bs_number, ®ion_code); + EXPECT_EQ(RegionCode::BS(), region_code); + + PhoneNumber us_number; + us_number.set_country_code(1); + us_number.set_national_number(4241231234ULL); + phone_util_.GetRegionCodeForNumber(us_number, ®ion_code); + EXPECT_EQ(RegionCode::US(), region_code); + + PhoneNumber gb_mobile; + gb_mobile.set_country_code(44); + gb_mobile.set_national_number(7912345678ULL); + phone_util_.GetRegionCodeForNumber(gb_mobile, ®ion_code); + EXPECT_EQ(RegionCode::GB(), region_code); + + PhoneNumber intl_toll_free_number; + intl_toll_free_number.set_country_code(800); + intl_toll_free_number.set_national_number(12345678ULL); + phone_util_.GetRegionCodeForNumber(intl_toll_free_number, ®ion_code); + EXPECT_EQ(RegionCode::UN001(), region_code); + + PhoneNumber universal_premium_rate; + universal_premium_rate.set_country_code(979); + universal_premium_rate.set_national_number(123456789ULL); + phone_util_.GetRegionCodeForNumber(universal_premium_rate, ®ion_code); + EXPECT_EQ(RegionCode::UN001(), region_code); +} + TEST_F(PhoneNumberUtilTest, IsPossibleNumber) { PhoneNumber number; number.set_country_code(1); @@ -1469,7 +1528,7 @@ TEST_F(PhoneNumberUtilTest, IsPossibleNumberWithReason) { EXPECT_EQ(PhoneNumberUtil::IS_POSSIBLE, phone_util_.IsPossibleNumberWithReason(ad_number)); ad_number.set_country_code(376); - ad_number.set_national_number(13ULL); + ad_number.set_national_number(1ULL); EXPECT_EQ(PhoneNumberUtil::TOO_SHORT, phone_util_.IsPossibleNumberWithReason(ad_number)); ad_number.set_country_code(376); @@ -1827,6 +1886,27 @@ TEST_F(PhoneNumberUtilTest, FormatInOriginalFormat) { phone_util_.FormatInOriginalFormat(phone_number, RegionCode::AU(), &formatted_number); EXPECT_EQ("0011 1 650 253 0000", formatted_number); + + // Test the star sign is not removed from or added to the original input by + // this method. + phone_number.Clear(); + formatted_number.clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.ParseAndKeepRawInput("*1234", + RegionCode::JP(), + &phone_number)); + phone_util_.FormatInOriginalFormat(phone_number, RegionCode::JP(), + &formatted_number); + EXPECT_EQ("*1234", formatted_number); + phone_number.Clear(); + formatted_number.clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.ParseAndKeepRawInput("1234", + RegionCode::JP(), + &phone_number)); + phone_util_.FormatInOriginalFormat(phone_number, RegionCode::JP(), + &formatted_number); + EXPECT_EQ("1234", formatted_number); } TEST_F(PhoneNumberUtilTest, IsPremiumRate) { @@ -1850,6 +1930,10 @@ TEST_F(PhoneNumberUtilTest, IsPremiumRate) { number.set_country_code(49); number.set_national_number(90091234567ULL); EXPECT_EQ(PhoneNumberUtil::PREMIUM_RATE, phone_util_.GetNumberType(number)); + + number.set_country_code(979); + number.set_national_number(123456789ULL); + EXPECT_EQ(PhoneNumberUtil::PREMIUM_RATE, phone_util_.GetNumberType(number)); } TEST_F(PhoneNumberUtilTest, IsTollFree) { @@ -2047,7 +2131,7 @@ TEST_F(PhoneNumberUtilTest, ConvertAlphaCharactersInNumber) { } TEST_F(PhoneNumberUtilTest, NormaliseRemovePunctuation) { - string input_number("034-56&+#234"); + string input_number("034-56&+#2" "\xC2\xAD" "34"); Normalize(&input_number); static const string kExpectedOutput("03456234"); EXPECT_EQ(kExpectedOutput, input_number) @@ -2389,6 +2473,9 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchMatches) { EXPECT_EQ(PhoneNumberUtil::EXACT_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+64 3 331-6005", "+6433316005")); + EXPECT_EQ(PhoneNumberUtil::EXACT_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005", "tel:+64-3-331-6005;isub=123")); // Test alpha numbers. EXPECT_EQ(PhoneNumberUtil::EXACT_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+1800 siX-Flags", @@ -2439,7 +2526,7 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchMatches) { phone_util_.IsNumberMatch(br_number_1, br_number_2)); } -TEST_F(PhoneNumberUtilTest, IsNumberMatchNonMetches) { +TEST_F(PhoneNumberUtilTest, IsNumberMatchNonMatches) { // NSN matches. EXPECT_EQ(PhoneNumberUtil::NO_MATCH, phone_util_.IsNumberMatchWithTwoStrings("03 331 6005", @@ -2459,6 +2546,9 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchNonMetches) { EXPECT_EQ(PhoneNumberUtil::NO_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+64 3 331-6005 extn 1234", "+0116433316005#1235")); + EXPECT_EQ(PhoneNumberUtil::NO_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005 extn 1234", "tel:+64-3-331-6005;ext=1235")); // NSN matches, but extension is different - not the same number. EXPECT_EQ(PhoneNumberUtil::NO_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+64 3 331-6005 ext.1235", @@ -2480,6 +2570,10 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchNsnMatches) { EXPECT_EQ(PhoneNumberUtil::NSN_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+64 3 331-6005", "03 331 6005")); + EXPECT_EQ(PhoneNumberUtil::NSN_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005", + "tel:03-331-6005;isub=1234;phone-context=abc.nz")); PhoneNumber nz_number; nz_number.set_country_code(64); @@ -2529,6 +2623,17 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchShortNsnMatches) { EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, phone_util_.IsNumberMatchWithTwoStrings("+64 3 331-6005", "331 6005")); + EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005", "tel:331-6005;phone-context=abc.nz")); + EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005", + "tel:331-6005;isub=1234;phone-context=abc.nz")); + EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "+64 3 331-6005", + "tel:331-6005;isub=1234;phone-context=abc.nz;a=%A1")); // We did not know that the "0" was a national prefix since neither number has // a country code, so this is considered a SHORT_NSN_MATCH. @@ -2540,6 +2645,9 @@ TEST_F(PhoneNumberUtilTest, IsNumberMatchShortNsnMatches) { phone_util_.IsNumberMatchWithTwoStrings("3 331-6005", "331 6005")); + EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, + phone_util_.IsNumberMatchWithTwoStrings( + "3 331-6005", "tel:331-6005;phone-context=abc.nz")); EXPECT_EQ(PhoneNumberUtil::SHORT_NSN_MATCH, phone_util_.IsNumberMatchWithTwoStrings("3 331-6005", "+64 331 6005")); @@ -2614,7 +2722,21 @@ TEST_F(PhoneNumberUtilTest, ParseNationalNumber) { phone_util_.Parse("tel:331-6005;phone-context=+64-3", RegionCode::US(), &test_number)); EXPECT_EQ(nz_number, test_number); - + // Test parsing RFC3966 format with optional user-defined parameters. The + // parameters will appear after the context if present. + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:03-331-6005;phone-context=+64;a=%A1", + RegionCode::NZ(), &test_number)); + EXPECT_EQ(nz_number, test_number); + // Test parsing RFC3966 with an ISDN subaddress. + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:03-331-6005;isub=12345;phone-context=+64", + RegionCode::NZ(), &test_number)); + EXPECT_EQ(nz_number, test_number); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:+64-3-331-6005;isub=12345", + RegionCode::US(), &test_number)); + EXPECT_EQ(nz_number, test_number); // Testing international prefixes. // Should strip country code. test_number.Clear(); @@ -2652,6 +2774,34 @@ TEST_F(PhoneNumberUtilTest, ParseNationalNumber) { RegionCode::NZ(), &test_number)); EXPECT_EQ(nz_number, test_number); + PhoneNumber us_local_number; + us_local_number.set_country_code(1); + us_local_number.set_national_number(2530000ULL); + test_number.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:253-0000;phone-context=www.google.com", + RegionCode::US(), &test_number)); + EXPECT_EQ(us_local_number, test_number); + test_number.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse( + "tel:253-0000;isub=12345;phone-context=www.google.com", + RegionCode::US(), &test_number)); + EXPECT_EQ(us_local_number, test_number); + // This is invalid because no "+" sign is present as part of phone-context. + // The phone context is simply ignored in this case just as if it contains a + // domain. + test_number.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:2530000;isub=12345;phone-context=1-650", + RegionCode::US(), &test_number)); + EXPECT_EQ(us_local_number, test_number); + test_number.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:2530000;isub=12345;phone-context=1234.com", + RegionCode::US(), &test_number)); + EXPECT_EQ(us_local_number, test_number); + // Test for http://b/issue?id=2247493 nz_number.Clear(); nz_number.set_country_code(64); @@ -2789,6 +2939,12 @@ TEST_F(PhoneNumberUtilTest, ParseWithInternationalPrefixes) { phone_util_.Parse("\xEF\xBC\x8B" "1 (650) 333-6000", /* "+1 (650) 333-6000" */ RegionCode::SG(), &test_number)); + // Using a soft hyphen U+00AD. + test_number.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("1 (650) 333" "\xC2\xAD" "-6000", + /* "1 (650) 333­-6000­" */ + RegionCode::US(), &test_number)); EXPECT_EQ(us_number, test_number); // The whole number, including punctuation, is here represented in full-width // form. @@ -3066,15 +3222,15 @@ TEST_F(PhoneNumberUtilTest, FailedParseOnInvalidNumbers) { EXPECT_EQ(PhoneNumber::default_instance(), test_number); // RFC3966 phone-context is a website. - EXPECT_EQ(PhoneNumberUtil::NOT_A_NUMBER, - phone_util_.Parse("tel:555-1234;phone-context:www.google.com", - RegionCode::US(), &test_number)); + EXPECT_EQ(PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR, + phone_util_.Parse("tel:555-1234;phone-context=www.google.com", + RegionCode::ZZ(), &test_number)); EXPECT_EQ(PhoneNumber::default_instance(), test_number); // This is invalid because no "+" sign is present as part of phone-context. // This should not succeed in being parsed. - EXPECT_EQ(PhoneNumberUtil::NOT_A_NUMBER, - phone_util_.Parse("tel:555-1234;phone-context:1-331", - RegionCode::US(), &test_number)); + EXPECT_EQ(PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR, + phone_util_.Parse("tel:555-1234;phone-context=1-331", + RegionCode::ZZ(), &test_number)); EXPECT_EQ(PhoneNumber::default_instance(), test_number); } @@ -3112,6 +3268,15 @@ TEST_F(PhoneNumberUtilTest, ParseNumbersWithPlusWithNoRegion) { RegionCode::GetUnknown(), &result_proto)); EXPECT_EQ(toll_free_number, result_proto); + PhoneNumber universal_premium_rate; + universal_premium_rate.set_country_code(979); + universal_premium_rate.set_national_number(123456789ULL); + result_proto.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("+979 123 456 789", + RegionCode::GetUnknown(), &result_proto)); + EXPECT_EQ(universal_premium_rate, result_proto); + result_proto.Clear(); // Test parsing RFC3966 format with a phone context. EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, @@ -3123,6 +3288,11 @@ TEST_F(PhoneNumberUtilTest, ParseNumbersWithPlusWithNoRegion) { phone_util_.Parse(" tel:03-331-6005;phone-context=+64", RegionCode::GetUnknown(), &result_proto)); EXPECT_EQ(nz_number, result_proto); + result_proto.Clear(); + EXPECT_EQ(PhoneNumberUtil::NO_PARSING_ERROR, + phone_util_.Parse("tel:03-331-6005;isub=12345;phone-context=+64", + RegionCode::GetUnknown(), &result_proto)); + EXPECT_EQ(nz_number, result_proto); nz_number.set_raw_input("+64 3 331 6005"); nz_number.set_country_code_source(PhoneNumber::FROM_NUMBER_WITH_PLUS_SIGN); @@ -3451,15 +3621,15 @@ TEST_F(PhoneNumberUtilTest, IsAlphaNumber) { EXPECT_TRUE(phone_util_.IsAlphaNumber(kAlphaNumber)); static const string kAlphaNumberWithExtension = "1800 six-flags ext. 1234"; EXPECT_TRUE(phone_util_.IsAlphaNumber(kAlphaNumberWithExtension)); - static const string kI18NAlphaNumber("+800 six-flags"); - EXPECT_TRUE(phone_util_.IsAlphaNumber(kI18NAlphaNumber)); + static const string kI18nAlphaNumber("+800 six-flags"); + EXPECT_TRUE(phone_util_.IsAlphaNumber(kI18nAlphaNumber)); static const string kNonAlphaNumber("1800 123-1234"); EXPECT_FALSE(phone_util_.IsAlphaNumber(kNonAlphaNumber)); static const string kNonAlphaNumberWithExtension( "1800 123-1234 extension: 1234"); EXPECT_FALSE(phone_util_.IsAlphaNumber(kNonAlphaNumberWithExtension)); - static const string kI18NNonAlphaNumber("+800 1234-1234"); - EXPECT_FALSE(phone_util_.IsAlphaNumber(kI18NNonAlphaNumber)); + static const string kI18nNonAlphaNumber("+800 1234-1234"); + EXPECT_FALSE(phone_util_.IsAlphaNumber(kI18nNonAlphaNumber)); } } // namespace phonenumbers diff --git a/cpp/test/phonenumbers/shortnumberutil_test.cc b/cpp/test/phonenumbers/shortnumberutil_test.cc new file mode 100644 index 000000000..663517bed --- /dev/null +++ b/cpp/test/phonenumbers/shortnumberutil_test.cc @@ -0,0 +1,161 @@ +// Copyright (C) 2009 The Libphonenumber Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Author: David Yonge-Mallo + +#include + +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/shortnumberutil.h" +#include "phonenumbers/test_util.h" + +namespace i18n { +namespace phonenumbers { + +class ShortNumberUtilTest : public testing::Test { + protected: + ShortNumberUtilTest() : short_util_() { + } + + const ShortNumberUtil short_util_; + + private: + DISALLOW_COPY_AND_ASSIGN(ShortNumberUtilTest); +}; + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumber_US) { + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("911", RegionCode::US())); + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("119", RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("999", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumberLongNumber_US) { + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("9116666666", + RegionCode::US())); + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("1196666666", + RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("9996666666", + RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumberWithFormatting_US) { + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("9-1-1", RegionCode::US())); + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("1-1-9", RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("9-9-9", + RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumberWithPlusSign_US) { + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("+911", RegionCode::US())); + // This hex sequence is the full-width plus sign U+FF0B. + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("\xEF\xBC\x8B" "911", + RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber(" +911", + RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("+119", RegionCode::US())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("+999", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumber_BR) { + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("911", RegionCode::BR())); + EXPECT_TRUE(short_util_.ConnectsToEmergencyNumber("190", RegionCode::BR())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("999", RegionCode::BR())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumberLongNumber_BR) { + // Brazilian emergency numbers don't work when additional digits are appended. + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("9111", RegionCode::BR())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("1900", RegionCode::BR())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("9996", RegionCode::BR())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumber_AO) { + // Angola doesn't have any metadata for emergency numbers in the test + // metadata. + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("911", RegionCode::AO())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("222123456", + RegionCode::AO())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("923123456", + RegionCode::AO())); +} + +TEST_F(ShortNumberUtilTest, ConnectsToEmergencyNumber_ZW) { + // Zimbabwe doesn't have any metadata in the test metadata. + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("911", RegionCode::ZW())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("01312345", + RegionCode::ZW())); + EXPECT_FALSE(short_util_.ConnectsToEmergencyNumber("0711234567", + RegionCode::ZW())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumber_US) { + EXPECT_TRUE(short_util_.IsEmergencyNumber("911", RegionCode::US())); + EXPECT_TRUE(short_util_.IsEmergencyNumber("119", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("999", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumberLongNumber_US) { + EXPECT_FALSE(short_util_.IsEmergencyNumber("9116666666", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("1196666666", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("9996666666", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumberWithFormatting_US) { + EXPECT_TRUE(short_util_.IsEmergencyNumber("9-1-1", RegionCode::US())); + EXPECT_TRUE(short_util_.IsEmergencyNumber("*911", RegionCode::US())); + EXPECT_TRUE(short_util_.IsEmergencyNumber("1-1-9", RegionCode::US())); + EXPECT_TRUE(short_util_.IsEmergencyNumber("*119", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("9-9-9", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("*999", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumberWithPlusSign_US) { + EXPECT_FALSE(short_util_.IsEmergencyNumber("+911", RegionCode::US())); + // This hex sequence is the full-width plus sign U+FF0B. + EXPECT_FALSE(short_util_.IsEmergencyNumber("\xEF\xBC\x8B" "911", + RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber(" +911", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("+119", RegionCode::US())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("+999", RegionCode::US())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumber_BR) { + EXPECT_TRUE(short_util_.IsEmergencyNumber("911", RegionCode::BR())); + EXPECT_TRUE(short_util_.IsEmergencyNumber("190", RegionCode::BR())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("999", RegionCode::BR())); +} + +TEST_F(ShortNumberUtilTest, EmergencyNumberLongNumber_BR) { + EXPECT_FALSE(short_util_.IsEmergencyNumber("9111", RegionCode::BR())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("1900", RegionCode::BR())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("9996", RegionCode::BR())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumber_AO) { + // Angola doesn't have any metadata for emergency numbers in the test + // metadata. + EXPECT_FALSE(short_util_.IsEmergencyNumber("911", RegionCode::AO())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("222123456", RegionCode::AO())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("923123456", RegionCode::AO())); +} + +TEST_F(ShortNumberUtilTest, IsEmergencyNumber_ZW) { + // Zimbabwe doesn't have any metadata in the test metadata. + EXPECT_FALSE(short_util_.IsEmergencyNumber("911", RegionCode::ZW())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("01312345", RegionCode::ZW())); + EXPECT_FALSE(short_util_.IsEmergencyNumber("0711234567", RegionCode::ZW())); +} + +} // namespace phonenumbers +} // namespace i18n diff --git a/cpp/test/phonenumbers/test_util.h b/cpp/test/phonenumbers/test_util.h index 5e65aec79..0a3957dac 100644 --- a/cpp/test/phonenumbers/test_util.h +++ b/cpp/test/phonenumbers/test_util.h @@ -141,6 +141,10 @@ class RegionCode { return "YT"; } + static const char* ZW() { + return "ZW"; + } + // Returns a region code string representing the "unknown" region. static const char* GetUnknown() { return "ZZ";