blob: f19c637fccfaeea422d19d40b8b8a8e3478314cf [file] [log] [blame]
// Copyright (C) 2011 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: Lara Rennie
#include "phonenumbers/phonenumbermatcher.h"
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include <unicode/unistr.h>
#include "phonenumbers/base/basictypes.h"
#include "phonenumbers/base/memory/scoped_ptr.h"
#include "phonenumbers/base/memory/singleton.h"
#include "phonenumbers/default_logger.h"
#include "phonenumbers/phonenumber.h"
#include "phonenumbers/phonenumber.pb.h"
#include "phonenumbers/phonenumbermatch.h"
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/stringutil.h"
#include "phonenumbers/test_util.h"
namespace i18n {
namespace phonenumbers {
using std::string;
using std::vector;
using icu::UnicodeString;
namespace {
// Small class that holds the context of the number we are testing against. The
// test will insert the phone number to be found between leading_text and
// trailing_text.
struct NumberContext {
string leading_text_;
string trailing_text_;
NumberContext(const string& leading_text, const string& trailing_text)
: leading_text_(leading_text),
trailing_text_(trailing_text) {
}
};
// Small class that holds the number we want to test and the region for which it
// should be valid.
struct NumberTest {
string raw_string_;
string region_;
string ToString() const {
return StrCat(raw_string_, " (", region_, ")");
}
NumberTest(const string& raw_string, const string& region)
: raw_string_(raw_string),
region_(region) {
}
};
} // namespace
class PhoneNumberMatcherTest : public testing::Test {
protected:
PhoneNumberMatcherTest()
: phone_util_(*PhoneNumberUtil::GetInstance()),
matcher_(phone_util_, "",
RegionCode::US(),
PhoneNumberMatcher::VALID, 5),
offset_(0) {
PhoneNumberUtil::GetInstance()->SetLogger(new StdoutLogger());
}
bool IsLatinLetter(char32 letter) {
return PhoneNumberMatcher::IsLatinLetter(letter);
}
bool ExtractMatch(const string& text, PhoneNumberMatch* match) {
return matcher_.ExtractMatch(text, offset_, match);
}
PhoneNumberMatcher* GetMatcherWithLeniency(
const string& text, const string& region,
PhoneNumberMatcher::Leniency leniency) const {
return new PhoneNumberMatcher(phone_util_, text, region, leniency,
100 /* max_tries */);
}
// Tests each number in the test cases provided is found in its entirety for
// the specified leniency level.
void DoTestNumberMatchesForLeniency(
const vector<NumberTest>& test_cases,
PhoneNumberMatcher::Leniency leniency) const {
scoped_ptr<PhoneNumberMatcher> matcher;
for (vector<NumberTest>::const_iterator test = test_cases.begin();
test != test_cases.end(); ++test) {
matcher.reset(GetMatcherWithLeniency(
test->raw_string_, test->region_, leniency));
EXPECT_TRUE(matcher->HasNext())
<< "No match found in " << test->ToString()
<< " for leniency: " << leniency;
if (matcher->HasNext()) {
PhoneNumberMatch match;
matcher->Next(&match);
EXPECT_EQ(test->raw_string_, match.raw_string())
<< "Found wrong match in test " << test->ToString()
<< ". Found " << match.raw_string();
}
}
}
// Tests no number in the test cases provided is found for the specified
// leniency level.
void DoTestNumberNonMatchesForLeniency(
const vector<NumberTest>& test_cases,
PhoneNumberMatcher::Leniency leniency) const {
scoped_ptr<PhoneNumberMatcher> matcher;
for (vector<NumberTest>::const_iterator test = test_cases.begin();
test != test_cases.end(); ++test) {
matcher.reset(GetMatcherWithLeniency(
test->raw_string_, test->region_, leniency));
EXPECT_FALSE(matcher->HasNext()) << "Match found in " << test->ToString()
<< " for leniency: " << leniency;
}
}
// Asserts that another number can be found in "text" starting at "index", and
// that its corresponding range is [start, end).
void AssertEqualRange(const string& text, int index, int start, int end) {
string sub = text.substr(index);
PhoneNumberMatcher matcher(phone_util_, sub, RegionCode::NZ(),
PhoneNumberMatcher::POSSIBLE,
1000000 /* max_tries */);
PhoneNumberMatch match;
ASSERT_TRUE(matcher.HasNext());
matcher.Next(&match);
EXPECT_EQ(start - index, match.start());
EXPECT_EQ(end - index, match.end());
EXPECT_EQ(sub.substr(match.start(), match.length()), match.raw_string());
}
// Tests numbers found by the PhoneNumberMatcher in various textual contexts.
void DoTestFindInContext(const string& number,
const string& default_country) {
FindPossibleInContext(number, default_country);
PhoneNumber parsed;
phone_util_.Parse(number, default_country, &parsed);
if (phone_util_.IsValidNumber(parsed)) {
FindValidInContext(number, default_country);
}
}
// Helper method which tests the contexts provided and ensures that:
// -- if is_valid is true, they all find a test number inserted in the middle
// when leniency of matching is set to VALID; else no test number should be
// extracted at that leniency level
// -- if is_possible is true, they all find a test number inserted in the
// middle when leniency of matching is set to POSSIBLE; else no test number
// should be extracted at that leniency level
void FindMatchesInContexts(const vector<NumberContext>& contexts,
bool is_valid, bool is_possible,
const string& region, const string& number) {
if (is_valid) {
DoTestInContext(number, region, contexts, PhoneNumberMatcher::VALID);
} else {
for (vector<NumberContext>::const_iterator it = contexts.begin();
it != contexts.end(); ++it) {
string text = StrCat(it->leading_text_, number, it->trailing_text_);
PhoneNumberMatcher matcher(text, region);
EXPECT_FALSE(matcher.HasNext());
}
}
if (is_possible) {
DoTestInContext(number, region, contexts, PhoneNumberMatcher::POSSIBLE);
} else {
for (vector<NumberContext>::const_iterator it = contexts.begin();
it != contexts.end(); ++it) {
string text = StrCat(it->leading_text_, number, it->trailing_text_);
PhoneNumberMatcher matcher(phone_util_, text, region,
PhoneNumberMatcher::POSSIBLE,
10000); // Number of matches.
EXPECT_FALSE(matcher.HasNext());
}
}
}
// Variant of FindMatchesInContexts that uses a default number and region.
void FindMatchesInContexts(const vector<NumberContext>& contexts,
bool is_valid, bool is_possible) {
const string& region = RegionCode::US();
const string number("415-666-7777");
FindMatchesInContexts(contexts, is_valid, is_possible, region, number);
}
// Tests valid numbers in contexts that should pass for
// PhoneNumberMatcher::POSSIBLE.
void FindPossibleInContext(const string& number,
const string& default_country) {
vector<NumberContext> context_pairs;
context_pairs.push_back(NumberContext("", "")); // no context
context_pairs.push_back(NumberContext(" ", "\t")); // whitespace only
context_pairs.push_back(NumberContext("Hello ", "")); // no context at end
// No context at start.
context_pairs.push_back(NumberContext("", " to call me!"));
context_pairs.push_back(NumberContext("Hi there, call ", " to reach me!"));
// With commas.
context_pairs.push_back(NumberContext("Hi there, call ", ", or don't"));
// Three examples without whitespace around the number.
context_pairs.push_back(NumberContext("Hi call", ""));
context_pairs.push_back(NumberContext("", "forme"));
context_pairs.push_back(NumberContext("Hi call", "forme"));
// With other small numbers.
context_pairs.push_back(NumberContext("It's cheap! Call ", " before 6:30"));
// With a second number later.
context_pairs.push_back(NumberContext("Call ", " or +1800-123-4567!"));
// With a Month-Day date.
context_pairs.push_back(NumberContext("Call me on June 2 at", ""));
// With publication pages.
context_pairs.push_back(NumberContext(
"As quoted by Alfonso 12-15 (2009), you may call me at ", ""));
context_pairs.push_back(NumberContext(
"As quoted by Alfonso et al. 12-15 (2009), you may call me at ", ""));
// With dates, written in the American style.
context_pairs.push_back(NumberContext(
"As I said on 03/10/2011, you may call me at ", ""));
// With trailing numbers after a comma. The 45 should not be considered an
// extension.
context_pairs.push_back(NumberContext("", ", 45 days a year"));
// With a postfix stripped off as it looks like the start of another number.
context_pairs.push_back(NumberContext("Call ", "/x12 more"));
DoTestInContext(number, default_country, context_pairs,
PhoneNumberMatcher::POSSIBLE);
}
// Tests valid numbers in contexts that fail for PhoneNumberMatcher::POSSIBLE
// but are valid for PhoneNumberMatcher::VALID.
void FindValidInContext(const string& number, const string& default_country) {
vector<NumberContext> context_pairs;
// With other small numbers.
context_pairs.push_back(NumberContext("It's only 9.99! Call ", " to buy"));
// With a number Day.Month.Year date.
context_pairs.push_back(NumberContext("Call me on 21.6.1984 at ", ""));
// With a number Month/Day date.
context_pairs.push_back(NumberContext("Call me on 06/21 at ", ""));
// With a number Day.Month date.
context_pairs.push_back(NumberContext("Call me on 21.6. at ", ""));
// With a number Month/Day/Year date.
context_pairs.push_back(NumberContext("Call me on 06/21/84 at ", ""));
DoTestInContext(number, default_country, context_pairs,
PhoneNumberMatcher::VALID);
}
void DoTestInContext(const string& number, const string& default_country,
const vector<NumberContext>& context_pairs,
PhoneNumberMatcher::Leniency leniency) {
for (vector<NumberContext>::const_iterator it = context_pairs.begin();
it != context_pairs.end(); ++it) {
string prefix = it->leading_text_;
string text = StrCat(prefix, number, it->trailing_text_);
int start = prefix.length();
int end = start + number.length();
PhoneNumberMatcher matcher(phone_util_, text, default_country, leniency,
1000000 /* max_tries */);
PhoneNumberMatch match;
ASSERT_TRUE(matcher.HasNext())
<< "Did not find a number in '" << text << "'; expected '"
<< number << "'";
matcher.Next(&match);
string extracted = text.substr(match.start(), match.length());
EXPECT_EQ(start, match.start());
EXPECT_EQ(end, match.end());
EXPECT_EQ(number, extracted);
EXPECT_EQ(extracted, match.raw_string())
<< "Unexpected phone region in '" << text << "'; extracted '"
<< extracted << "'";
EnsureTermination(text, default_country, leniency);
}
}
// Exhaustively searches for phone numbers from each index within "text" to
// test that finding matches always terminates.
void EnsureTermination(const string& text, const string& default_country,
PhoneNumberMatcher::Leniency leniency) {
for (size_t index = 0; index <= text.length(); ++index) {
string sub = text.substr(index);
// Iterates over all matches.
PhoneNumberMatcher matcher(phone_util_, text, default_country, leniency,
1000000 /* max_tries */);
string matches;
PhoneNumberMatch match;
int match_count = 0;
while (matcher.HasNext()) {
matcher.Next(&match);
StrAppend(&matches, ",", match.ToString());
++match_count;
}
// We should not ever find more than 10 matches in a single candidate text
// in these test cases, so we check here that the matcher was limited by
// the number of matches, rather than by max_tries.
ASSERT_LT(match_count, 10);
}
}
const PhoneNumberUtil& phone_util_;
private:
PhoneNumberMatcher matcher_;
int offset_;
};
// See PhoneNumberUtilTest::ParseNationalNumber.
TEST_F(PhoneNumberMatcherTest, FindNationalNumber) {
// Same cases as in ParseNationalNumber.
DoTestFindInContext("033316005", RegionCode::NZ());
// "33316005", RegionCode::NZ() is omitted since the national-prefix is
// obligatory for these types of numbers in New Zealand.
// National prefix attached and some formatting present.
DoTestFindInContext("03-331 6005", RegionCode::NZ());
DoTestFindInContext("03 331 6005", RegionCode::NZ());
// Testing international prefixes.
// Should strip country code.
DoTestFindInContext("0064 3 331 6005", RegionCode::NZ());
// Try again, but this time we have an international number with Region Code
// US. It should recognize the country code and parse accordingly.
DoTestFindInContext("01164 3 331 6005", RegionCode::US());
DoTestFindInContext("+64 3 331 6005", RegionCode::US());
DoTestFindInContext("64(0)64123456", RegionCode::NZ());
// Check that using a "/" is fine in a phone number.
DoTestFindInContext("123/45678", RegionCode::DE());
DoTestFindInContext("123-456-7890", RegionCode::US());
}
// See PhoneNumberUtilTest::ParseWithInternationalPrefixes.
TEST_F(PhoneNumberMatcherTest, FindWithInternationalPrefixes) {
DoTestFindInContext("+1 (650) 333-6000", RegionCode::NZ());
DoTestFindInContext("1-650-333-6000", RegionCode::US());
// Calling the US number from Singapore by using different service providers
// 1st test: calling using SingTel IDD service (IDD is 001)
DoTestFindInContext("0011-650-333-6000", RegionCode::SG());
// 2nd test: calling using StarHub IDD service (IDD is 008)
DoTestFindInContext("0081-650-333-6000", RegionCode::SG());
// 3rd test: calling using SingTel V019 service (IDD is 019)
DoTestFindInContext("0191-650-333-6000", RegionCode::SG());
// Calling the US number from Poland
DoTestFindInContext("0~01-650-333-6000", RegionCode::PL());
// Using "++" at the start.
DoTestFindInContext("++1 (650) 333-6000", RegionCode::PL());
// Using a full-width plus sign.
DoTestFindInContext(
"\xEF\xBC\x8B""1 (650) 333-6000" /* "+1 (650) 333-6000" */,
RegionCode::SG());
// The whole number, including punctuation, is here represented in full-width
// form.
DoTestFindInContext(
/* "+1 (650) 333-6000" */
"\xEF\xBC\x8B\xEF\xBC\x91\xE3\x80\x80\xEF\xBC\x88\xEF\xBC\x96\xEF\xBC\x95"
"\xEF\xBC\x90\xEF\xBC\x89\xE3\x80\x80\xEF\xBC\x93\xEF\xBC\x93\xEF\xBC\x93"
"\xEF\xBC\x8D\xEF\xBC\x96\xEF\xBC\x90\xEF\xBC\x90\xEF\xBC\x90",
RegionCode::SG());
}
// See PhoneNumberUtilTest::ParseWithLeadingZero.
TEST_F(PhoneNumberMatcherTest, FindWithLeadingZero) {
DoTestFindInContext("+39 02-36618 300", RegionCode::NZ());
DoTestFindInContext("02-36618 300", RegionCode::IT());
DoTestFindInContext("312 345 678", RegionCode::IT());
}
// See PhoneNumberUtilTest::ParseNationalNumberArgentina.
TEST_F(PhoneNumberMatcherTest, FindNationalNumberArgentina) {
// Test parsing mobile numbers of Argentina.
DoTestFindInContext("+54 9 343 555 1212", RegionCode::AR());
DoTestFindInContext("0343 15 555 1212", RegionCode::AR());
DoTestFindInContext("+54 9 3715 65 4320", RegionCode::AR());
DoTestFindInContext("03715 15 65 4320", RegionCode::AR());
// Test parsing fixed-line numbers of Argentina.
DoTestFindInContext("+54 11 3797 0000", RegionCode::AR());
DoTestFindInContext("011 3797 0000", RegionCode::AR());
DoTestFindInContext("+54 3715 65 4321", RegionCode::AR());
DoTestFindInContext("03715 65 4321", RegionCode::AR());
DoTestFindInContext("+54 23 1234 0000", RegionCode::AR());
DoTestFindInContext("023 1234 0000", RegionCode::AR());
}
// See PhoneNumberMatcherTest::ParseWithXInNumber.
TEST_F(PhoneNumberMatcherTest, FindWithXInNumber) {
DoTestFindInContext("(0xx) 123456789", RegionCode::AR());
// A case where x denotes both carrier codes and extension symbol.
DoTestFindInContext("(0xx) 123456789 x 1234", RegionCode::AR());
// This test is intentionally constructed such that the number of digit after
// xx is larger than 7, so that the number won't be mistakenly treated as an
// extension, as we allow extensions up to 7 digits. This assumption is okay
// for now as all the countries where a carrier selection code is written in
// the form of xx have a national significant number of length larger than 7.
DoTestFindInContext("011xx5481429712", RegionCode::US());
}
// See PhoneNumberUtilTest::ParseNumbersMexico.
TEST_F(PhoneNumberMatcherTest, FindNumbersMexico) {
// Test parsing fixed-line numbers of Mexico.
DoTestFindInContext("+52 (449)978-0001", RegionCode::MX());
DoTestFindInContext("01 (449)978-0001", RegionCode::MX());
DoTestFindInContext("(449)978-0001", RegionCode::MX());
// Test parsing mobile numbers of Mexico.
DoTestFindInContext("+52 1 33 1234-5678", RegionCode::MX());
DoTestFindInContext("044 (33) 1234-5678", RegionCode::MX());
DoTestFindInContext("045 33 1234-5678", RegionCode::MX());
}
// See PhoneNumberUtilTest::ParseNumbersWithPlusWithNoRegion.
TEST_F(PhoneNumberMatcherTest, FindNumbersWithPlusWithNoRegion) {
// RegionCode::ZZ() is allowed only if the number starts with a '+' - then the
// country code can be calculated.
DoTestFindInContext("+64 3 331 6005", RegionCode::ZZ());
}
// See PhoneNumberUtilTest::ParseExtensions.
TEST_F(PhoneNumberMatcherTest, FindExtensions) {
DoTestFindInContext("03 331 6005 ext 3456", RegionCode::NZ());
DoTestFindInContext("03-3316005x3456", RegionCode::NZ());
DoTestFindInContext("03-3316005 int.3456", RegionCode::NZ());
DoTestFindInContext("03 3316005 #3456", RegionCode::NZ());
DoTestFindInContext("0~0 1800 7493 524", RegionCode::PL());
DoTestFindInContext("(1800) 7493.524", RegionCode::US());
// Check that the last instance of an extension token is matched.
DoTestFindInContext("0~0 1800 7493 524 ~1234", RegionCode::PL());
// Verifying bug-fix where the last digit of a number was previously omitted
// if it was a 0 when extracting the extension. Also verifying a few different
// cases of extensions.
DoTestFindInContext("+44 2034567890x456", RegionCode::NZ());
DoTestFindInContext("+44 2034567890x456", RegionCode::GB());
DoTestFindInContext("+44 2034567890 x456", RegionCode::GB());
DoTestFindInContext("+44 2034567890 X456", RegionCode::GB());
DoTestFindInContext("+44 2034567890 X 456", RegionCode::GB());
DoTestFindInContext("+44 2034567890 X 456", RegionCode::GB());
DoTestFindInContext("+44 2034567890 X 456", RegionCode::GB());
DoTestFindInContext("(800) 901-3355 x 7246433", RegionCode::US());
DoTestFindInContext("(800) 901-3355 , ext 7246433", RegionCode::US());
DoTestFindInContext("(800) 901-3355 ,extension 7246433", RegionCode::US());
// The next test differs from PhoneNumberUtil -> when matching we don't
// consider a lone comma to indicate an extension, although we accept it when
// parsing.
DoTestFindInContext("(800) 901-3355 ,x 7246433", RegionCode::US());
DoTestFindInContext("(800) 901-3355 ext: 7246433", RegionCode::US());
}
TEST_F(PhoneNumberMatcherTest, FindInterspersedWithSpace) {
DoTestFindInContext("0 3 3 3 1 6 0 0 5", RegionCode::NZ());
}
// Test matching behavior when starting in the middle of a phone number.
TEST_F(PhoneNumberMatcherTest, IntermediateParsePositions) {
string text = "Call 033316005 or 032316005!";
// | | | | | |
// 0 5 10 15 20 25
// Iterate over all possible indices.
for (int i = 0; i <= 5; ++i) {
AssertEqualRange(text, i, 5, 14);
}
// 7 and 8 digits in a row are still parsed as number.
AssertEqualRange(text, 6, 6, 14);
AssertEqualRange(text, 7, 7, 14);
// Anything smaller is skipped to the second instance.
for (int i = 8; i <= 19; ++i) {
AssertEqualRange(text, i, 19, 28);
}
}
TEST_F(PhoneNumberMatcherTest, MatchWithSurroundingZipcodes) {
string number = "415-666-7777";
string zip_preceding =
StrCat("My address is CA 34215 - ", number, " is my number.");
PhoneNumber expected_result;
phone_util_.Parse(number, RegionCode::US(), &expected_result);
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency(zip_preceding, RegionCode::US(),
PhoneNumberMatcher::VALID));
PhoneNumberMatch match;
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->Next(&match));
EXPECT_EQ(expected_result, match.number());
EXPECT_EQ(number, match.raw_string());
// Now repeat, but this time the phone number has spaces in it. It should
// still be found.
number = "(415) 666 7777";
string zip_following =
StrCat("My number is ", number, ". 34215 is my zip-code.");
matcher.reset(
GetMatcherWithLeniency(zip_following, RegionCode::US(),
PhoneNumberMatcher::VALID));
PhoneNumberMatch match_with_spaces;
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->Next(&match_with_spaces));
EXPECT_EQ(expected_result, match_with_spaces.number());
EXPECT_EQ(number, match_with_spaces.raw_string());
}
TEST_F(PhoneNumberMatcherTest, IsLatinLetter) {
EXPECT_TRUE(IsLatinLetter('c'));
EXPECT_TRUE(IsLatinLetter('C'));
EXPECT_TRUE(IsLatinLetter(UnicodeString::fromUTF8("\xC3\x89" /* "É" */)[0]));
// Combining acute accent.
EXPECT_TRUE(IsLatinLetter(UnicodeString::fromUTF8("\xCC\x81")[0]));
EXPECT_FALSE(IsLatinLetter(':'));
EXPECT_FALSE(IsLatinLetter('5'));
EXPECT_FALSE(IsLatinLetter('-'));
EXPECT_FALSE(IsLatinLetter('.'));
EXPECT_FALSE(IsLatinLetter(' '));
EXPECT_FALSE(
IsLatinLetter(UnicodeString::fromUTF8("\xE6\x88\x91" /* "我" */)[0]));
/* Hiragana letter no (の) - this should neither seem to start or end with a
Latin letter. */
EXPECT_FALSE(IsLatinLetter(UnicodeString::fromUTF8("\xE3\x81\xAE")[0]));
EXPECT_FALSE(IsLatinLetter(UnicodeString::fromUTF8("\xE3\x81\xAE")[2]));
}
TEST_F(PhoneNumberMatcherTest, MatchesWithSurroundingLatinChars) {
vector<NumberContext> possible_only_contexts;
possible_only_contexts.push_back(NumberContext("abc", "def"));
possible_only_contexts.push_back(NumberContext("abc", ""));
possible_only_contexts.push_back(NumberContext("", "def"));
possible_only_contexts.push_back(NumberContext("\xC3\x89" /* "É" */, ""));
// e with an acute accent decomposed (with combining mark).
possible_only_contexts.push_back(
NumberContext("\x20\x22\xCC\x81""e\xCC\x81" /* "́e\xCC\x81" */, ""));
// Numbers should not be considered valid, if they are surrounded by Latin
// characters, but should be considered possible.
FindMatchesInContexts(possible_only_contexts, false, true);
}
TEST_F(PhoneNumberMatcherTest, MoneyNotSeenAsPhoneNumber) {
vector<NumberContext> possible_only_contexts;
possible_only_contexts.push_back(NumberContext("$", ""));
possible_only_contexts.push_back(NumberContext("", "$"));
possible_only_contexts.push_back(NumberContext("\xC2\xA3" /* "£" */, ""));
possible_only_contexts.push_back(NumberContext("\xC2\xA5" /* "¥" */, ""));
FindMatchesInContexts(possible_only_contexts, false, true);
}
TEST_F(PhoneNumberMatcherTest, PercentageNotSeenAsPhoneNumber) {
vector<NumberContext> possible_only_contexts;
possible_only_contexts.push_back(NumberContext("", "%"));
// Numbers followed by % should be dropped.
FindMatchesInContexts(possible_only_contexts, false, true);
}
TEST_F(PhoneNumberMatcherTest, PhoneNumberWithLeadingOrTrailingMoneyMatches) {
vector<NumberContext> contexts;
contexts.push_back(NumberContext("$20 ", ""));
contexts.push_back(NumberContext("", " 100$"));
// Because of the space after the 20 (or before the 100) these dollar amounts
// should not stop the actual number from being found.
FindMatchesInContexts(contexts, true, true);
}
TEST_F(PhoneNumberMatcherTest,
MatchesWithSurroundingLatinCharsAndLeadingPunctuation) {
vector<NumberContext> possible_only_contexts;
// Contexts with trailing characters. Leading characters are okay here since
// the numbers we will insert start with punctuation, but trailing characters
// are still not allowed.
possible_only_contexts.push_back(NumberContext("abc", "def"));
possible_only_contexts.push_back(NumberContext("", "def"));
possible_only_contexts.push_back(NumberContext("", "\xC3\x89" /* "É" */));
// Numbers should not be considered valid, if they have trailing Latin
// characters, but should be considered possible.
string number_with_plus = "+14156667777";
string number_with_brackets = "(415)6667777";
FindMatchesInContexts(possible_only_contexts, false, true, RegionCode::US(),
number_with_plus);
FindMatchesInContexts(possible_only_contexts, false, true, RegionCode::US(),
number_with_brackets);
vector<NumberContext> valid_contexts;
valid_contexts.push_back(NumberContext("abc", ""));
valid_contexts.push_back(NumberContext("\xC3\x89" /* "É" */, ""));
valid_contexts.push_back(
NumberContext("\xC3\x89" /* "É" */, ".")); // Trailing punctuation.
// Trailing white-space.
valid_contexts.push_back(NumberContext("\xC3\x89" /* "É" */, " def"));
// Numbers should be considered valid, since they start with punctuation.
FindMatchesInContexts(valid_contexts, true, true, RegionCode::US(),
number_with_plus);
FindMatchesInContexts(valid_contexts, true, true, RegionCode::US(),
number_with_brackets);
}
TEST_F(PhoneNumberMatcherTest, MatchesWithSurroundingChineseChars) {
vector<NumberContext> valid_contexts;
valid_contexts.push_back(NumberContext(
/* "我的电话号码是" */
"\xE6\x88\x91\xE7\x9A\x84\xE7\x94\xB5\xE8\xAF\x9D\xE5\x8F\xB7\xE7\xA0\x81"
"\xE6\x98\xAF", ""));
valid_contexts.push_back(NumberContext(
"",
/* "是我的电话号码" */
"\xE6\x98\xAF\xE6\x88\x91\xE7\x9A\x84\xE7\x94\xB5\xE8\xAF\x9D\xE5\x8F\xB7"
"\xE7\xA0\x81"));
valid_contexts.push_back(NumberContext(
"\xE8\xAF\xB7\xE6\x8B\xA8\xE6\x89\x93" /* "请拨打" */,
"\xE6\x88\x91\xE5\x9C\xA8\xE6\x98\x8E\xE5\xA4\xA9" /* "我在明天" */));
// Numbers should be considered valid, since they are surrounded by Chinese.
FindMatchesInContexts(valid_contexts, true, true);
}
TEST_F(PhoneNumberMatcherTest, MatchesWithSurroundingPunctuation) {
vector<NumberContext> valid_contexts;
// At end of text.
valid_contexts.push_back(NumberContext("My number-", ""));
// At start of text.
valid_contexts.push_back(NumberContext("", ".Nice day."));
// Punctuation surround number.
valid_contexts.push_back(NumberContext("Tel:", "."));
// White-space is also fine.
valid_contexts.push_back(NumberContext("Tel: ", " on Saturdays."));
// Numbers should be considered valid, since they are surrounded by
// punctuation.
FindMatchesInContexts(valid_contexts, true, true);
}
TEST_F(PhoneNumberMatcherTest,
MatchesMultiplePhoneNumbersSeparatedByPhoneNumberPunctuation) {
const string text = "Call 650-253-4561 -- 455-234-3451";
const string& region = RegionCode::US();
PhoneNumber number1;
number1.set_country_code(phone_util_.GetCountryCodeForRegion(region));
number1.set_national_number(6502534561ULL);
PhoneNumberMatch match1(5, "650-253-4561", number1);
PhoneNumber number2;
number2.set_country_code(phone_util_.GetCountryCodeForRegion(region));
number2.set_national_number(4552343451ULL);
PhoneNumberMatch match2(21, "455-234-3451", number2);
PhoneNumberMatcher matcher(
phone_util_, text, region, PhoneNumberMatcher::VALID, 100);
PhoneNumberMatch actual_match1;
PhoneNumberMatch actual_match2;
matcher.Next(&actual_match1);
matcher.Next(&actual_match2);
EXPECT_TRUE(match1.Equals(actual_match1))
<< "Got: " << actual_match1.ToString();
EXPECT_TRUE(match2.Equals(actual_match2))
<< "Got: " << actual_match2.ToString();
}
TEST_F(PhoneNumberMatcherTest,
DoesNotMatchMultiplePhoneNumbersSeparatedWithNoWhiteSpace) {
const string text = "Call 650-253-4561--455-234-3451";
const string& region = RegionCode::US();
PhoneNumberMatcher matcher(
phone_util_, text, region, PhoneNumberMatcher::VALID, 100);
EXPECT_FALSE(matcher.HasNext());
}
// Strings with number-like things that shouldn't be found under any level.
static const NumberTest kImpossibleCases[] = {
NumberTest("12345", RegionCode::US()),
NumberTest("23456789", RegionCode::US()),
NumberTest("234567890112", RegionCode::US()),
NumberTest("650+253+1234", RegionCode::US()),
NumberTest("3/10/1984", RegionCode::CA()),
NumberTest("03/27/2011", RegionCode::US()),
NumberTest("31/8/2011", RegionCode::US()),
NumberTest("1/12/2011", RegionCode::US()),
NumberTest("10/12/82", RegionCode::DE()),
NumberTest("650x2531234", RegionCode::US()),
NumberTest("2012-01-02 08:00", RegionCode::US()),
NumberTest("2012/01/02 08:00", RegionCode::US()),
NumberTest("20120102 08:00", RegionCode::US()),
};
// Strings with number-like things that should only be found under "possible".
static const NumberTest kPossibleOnlyCases[] = {
// US numbers cannot start with 7 in the test metadata to be valid.
NumberTest("7121115678", RegionCode::US()),
// 'X' should not be found in numbers at leniencies stricter than POSSIBLE,
// unless it represents a carrier code or extension.
NumberTest("1650 x 253 - 1234", RegionCode::US()),
NumberTest("650 x 253 - 1234", RegionCode::US()),
NumberTest("6502531x234", RegionCode::US()),
NumberTest("(20) 3346 1234", RegionCode::GB()), // Non-optional NP omitted
};
// Strings with number-like things that should only be found up to and including
// the "valid" leniency level.
static const NumberTest kValidCases[] = {
NumberTest("65 02 53 00 00", RegionCode::US()),
NumberTest("6502 538365", RegionCode::US()),
// 2 slashes are illegal at higher levels.
NumberTest("650//253-1234", RegionCode::US()),
NumberTest("650/253/1234", RegionCode::US()),
NumberTest("9002309. 158", RegionCode::US()),
NumberTest("12 7/8 - 14 12/34 - 5", RegionCode::US()),
NumberTest("12.1 - 23.71 - 23.45", RegionCode::US()),
NumberTest("800 234 1 111x1111", RegionCode::US()),
NumberTest("1979-2011 100", RegionCode::US()),
// National number in wrong format.
NumberTest("+494949-4-94", RegionCode::DE()),
NumberTest(
/* "415666-7777" */
"\xEF\xBC\x94\xEF\xBC\x91\xEF\xBC\x95\xEF\xBC\x96\xEF\xBC\x96\xEF\xBC\x96"
"\x2D\xEF\xBC\x97\xEF\xBC\x97\xEF\xBC\x97\xEF\xBC\x97", RegionCode::US()),
NumberTest("2012-0102 08", RegionCode::US()), // Very strange formatting.
NumberTest("2012-01-02 08", RegionCode::US()),
// Breakdown assistance number with unexpected formatting.
NumberTest("1800-1-0-10 22", RegionCode::AU()),
NumberTest("030-3-2 23 12 34", RegionCode::DE()),
NumberTest("03 0 -3 2 23 12 34", RegionCode::DE()),
NumberTest("(0)3 0 -3 2 23 12 34", RegionCode::DE()),
NumberTest("0 3 0 -3 2 23 12 34", RegionCode::DE()),
};
// Strings with number-like things that should only be found up to and including
// the "strict_grouping" leniency level.
static const NumberTest kStrictGroupingCases[] = {
NumberTest("(415) 6667777", RegionCode::US()),
NumberTest("415-6667777", RegionCode::US()),
// Should be found by strict grouping but not exact grouping, as the last two
// groups are formatted together as a block.
NumberTest("0800-2491234", RegionCode::DE()),
// Doesn't match any formatting in the test file, but almost matches an
// alternate format (the last two groups have been squashed together here).
NumberTest("0900-1 123123", RegionCode::DE()),
NumberTest("(0)900-1 123123", RegionCode::DE()),
NumberTest("0 900-1 123123", RegionCode::DE()),
};
// Strings with number-like things that should be found at all levels.
static const NumberTest kExactGroupingCases[] = {
NumberTest(
/* "4156667777" */
"\xEF\xBC\x94\xEF\xBC\x91\xEF\xBC\x95\xEF\xBC\x96\xEF\xBC\x96\xEF\xBC\x96"
"\xEF\xBC\x97\xEF\xBC\x97\xEF\xBC\x97\xEF\xBC\x97", RegionCode::US()),
NumberTest(
/* "415-666-7777" */
"\xEF\xBC\x94\xEF\xBC\x91\xEF\xBC\x95\xEF\xBC\x8D\xEF\xBC\x96\xEF\xBC\x96"
"\xEF\xBC\x96\xEF\xBC\x8D\xEF\xBC\x97\xEF\xBC\x97\xEF\xBC\x97"
"\xEF\xBC\x97", RegionCode::US()),
NumberTest("4156667777", RegionCode::US()),
NumberTest("4156667777 x 123", RegionCode::US()),
NumberTest("415-666-7777", RegionCode::US()),
NumberTest("415/666-7777", RegionCode::US()),
NumberTest("415-666-7777 ext. 503", RegionCode::US()),
NumberTest("1 415 666 7777 x 123", RegionCode::US()),
NumberTest("+1 415-666-7777", RegionCode::US()),
NumberTest("+494949 49", RegionCode::DE()),
NumberTest("+49-49-34", RegionCode::DE()),
NumberTest("+49-4931-49", RegionCode::DE()),
NumberTest("04931-49", RegionCode::DE()), // With National Prefix
NumberTest("+49-494949", RegionCode::DE()), // One group with country code
NumberTest("+49-494949 ext. 49", RegionCode::DE()),
NumberTest("+49494949 ext. 49", RegionCode::DE()),
NumberTest("0494949", RegionCode::DE()),
NumberTest("0494949 ext. 49", RegionCode::DE()),
NumberTest("01 (33) 3461 2234", RegionCode::MX()), // Optional NP present
NumberTest("(33) 3461 2234", RegionCode::MX()), // Optional NP omitted
// Breakdown assistance number with normal formatting.
NumberTest("1800-10-10 22", RegionCode::AU()),
// Doesn't match any formatting in the test file, but matches an alternate
// format exactly.
NumberTest("0900-1 123 123", RegionCode::DE()),
NumberTest("(0)900-1 123 123", RegionCode::DE()),
NumberTest("0 900-1 123 123", RegionCode::DE()),
};
TEST_F(PhoneNumberMatcherTest, MatchesWithPossibleLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kPossibleOnlyCases,
kPossibleOnlyCases + arraysize(kPossibleOnlyCases));
test_cases.insert(test_cases.begin(), kValidCases,
kValidCases + arraysize(kValidCases));
test_cases.insert(test_cases.begin(), kStrictGroupingCases,
kStrictGroupingCases + arraysize(kStrictGroupingCases));
test_cases.insert(test_cases.begin(), kExactGroupingCases,
kExactGroupingCases + arraysize(kExactGroupingCases));
DoTestNumberMatchesForLeniency(test_cases, PhoneNumberMatcher::POSSIBLE);
}
TEST_F(PhoneNumberMatcherTest, NonMatchesWithPossibleLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kImpossibleCases,
kImpossibleCases + arraysize(kImpossibleCases));
DoTestNumberNonMatchesForLeniency(test_cases, PhoneNumberMatcher::POSSIBLE);
}
TEST_F(PhoneNumberMatcherTest, MatchesWithValidLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kValidCases,
kValidCases + arraysize(kValidCases));
test_cases.insert(test_cases.begin(), kStrictGroupingCases,
kStrictGroupingCases + arraysize(kStrictGroupingCases));
test_cases.insert(test_cases.begin(), kExactGroupingCases,
kExactGroupingCases + arraysize(kExactGroupingCases));
DoTestNumberMatchesForLeniency(test_cases, PhoneNumberMatcher::VALID);
}
TEST_F(PhoneNumberMatcherTest, NonMatchesWithValidLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kImpossibleCases,
kImpossibleCases + arraysize(kImpossibleCases));
test_cases.insert(test_cases.begin(), kPossibleOnlyCases,
kPossibleOnlyCases + arraysize(kPossibleOnlyCases));
DoTestNumberNonMatchesForLeniency(test_cases, PhoneNumberMatcher::VALID);
}
TEST_F(PhoneNumberMatcherTest, MatchesWithStrictGroupingLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kStrictGroupingCases,
kStrictGroupingCases + arraysize(kStrictGroupingCases));
test_cases.insert(test_cases.begin(), kExactGroupingCases,
kExactGroupingCases + arraysize(kExactGroupingCases));
DoTestNumberMatchesForLeniency(test_cases,
PhoneNumberMatcher::STRICT_GROUPING);
}
TEST_F(PhoneNumberMatcherTest, NonMatchesWithStrictGroupingLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kImpossibleCases,
kImpossibleCases + arraysize(kImpossibleCases));
test_cases.insert(test_cases.begin(), kPossibleOnlyCases,
kPossibleOnlyCases + arraysize(kPossibleOnlyCases));
test_cases.insert(test_cases.begin(), kValidCases,
kValidCases + arraysize(kValidCases));
DoTestNumberNonMatchesForLeniency(test_cases,
PhoneNumberMatcher::STRICT_GROUPING);
}
TEST_F(PhoneNumberMatcherTest, MatchesWithExactGroupingLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kExactGroupingCases,
kExactGroupingCases + arraysize(kExactGroupingCases));
DoTestNumberMatchesForLeniency(test_cases,
PhoneNumberMatcher::EXACT_GROUPING);
}
TEST_F(PhoneNumberMatcherTest, NonMatchesWithExactGroupingLeniency) {
vector<NumberTest> test_cases;
test_cases.insert(test_cases.begin(), kImpossibleCases,
kImpossibleCases + arraysize(kImpossibleCases));
test_cases.insert(test_cases.begin(), kPossibleOnlyCases,
kPossibleOnlyCases + arraysize(kPossibleOnlyCases));
test_cases.insert(test_cases.begin(), kValidCases,
kValidCases + arraysize(kValidCases));
test_cases.insert(test_cases.begin(), kStrictGroupingCases,
kStrictGroupingCases + arraysize(kStrictGroupingCases));
DoTestNumberNonMatchesForLeniency(test_cases,
PhoneNumberMatcher::EXACT_GROUPING);
}
TEST_F(PhoneNumberMatcherTest, ExtractMatchIgnoresAmericanDates) {
PhoneNumberMatch match;
string text = "As I said on 03/10/2011, you may call me at ";
EXPECT_FALSE(ExtractMatch(text, &match));
text = "As I said on 03/27/2011, you may call me at ";
EXPECT_FALSE(ExtractMatch(text, &match));
text = "As I said on 31/8/2011, you may call me at ";
EXPECT_FALSE(ExtractMatch(text, &match));
text = "As I said on 1/12/2011, you may call me at ";
EXPECT_FALSE(ExtractMatch(text, &match));
text = "I was born on 10/12/82. Please call me at ";
EXPECT_FALSE(ExtractMatch(text, &match));
}
TEST_F(PhoneNumberMatcherTest, NonMatchingBracketsAreInvalid) {
// The digits up to the ", " form a valid US number, but it shouldn't be
// matched as one since there was a non-matching bracket present.
scoped_ptr<PhoneNumberMatcher> matcher(GetMatcherWithLeniency(
"80.585 [79.964, 81.191]", RegionCode::US(),
PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
// The trailing "]" is thrown away before parsing, so the resultant number,
// while a valid US number, does not have matching brackets.
matcher.reset(GetMatcherWithLeniency(
"80.585 [79.964]", RegionCode::US(), PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
matcher.reset(GetMatcherWithLeniency(
"80.585 ((79.964)", RegionCode::US(), PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
// This case has too many sets of brackets to be valid.
matcher.reset(GetMatcherWithLeniency(
"(80).(585) (79).(9)64", RegionCode::US(), PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, NoMatchIfRegionIsUnknown) {
// Fail on non-international prefix if region code is ZZ.
scoped_ptr<PhoneNumberMatcher> matcher(GetMatcherWithLeniency(
"Random text body - number is 0331 6005, see you there",
RegionCode::ZZ(), PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, NoMatchInEmptyString) {
scoped_ptr<PhoneNumberMatcher> matcher(GetMatcherWithLeniency(
"", RegionCode::US(), PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
matcher.reset(GetMatcherWithLeniency(" ", RegionCode::US(),
PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, NoMatchIfNoNumber) {
scoped_ptr<PhoneNumberMatcher> matcher(GetMatcherWithLeniency(
"Random text body - number is foobar, see you there", RegionCode::US(),
PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, Sequences) {
// Test multiple occurrences.
const string text = "Call 033316005 or 032316005!";
const string& region = RegionCode::NZ();
PhoneNumber number1;
number1.set_country_code(phone_util_.GetCountryCodeForRegion(region));
number1.set_national_number(33316005ULL);
PhoneNumberMatch match1(5, "033316005", number1);
PhoneNumber number2;
number2.set_country_code(phone_util_.GetCountryCodeForRegion(region));
number2.set_national_number(32316005ULL);
PhoneNumberMatch match2(19, "032316005", number2);
PhoneNumberMatcher matcher(
phone_util_, text, region, PhoneNumberMatcher::POSSIBLE, 100);
PhoneNumberMatch actual_match1;
PhoneNumberMatch actual_match2;
matcher.Next(&actual_match1);
matcher.Next(&actual_match2);
EXPECT_TRUE(match1.Equals(actual_match1));
EXPECT_TRUE(match2.Equals(actual_match2));
}
TEST_F(PhoneNumberMatcherTest, MaxMatches) {
// Set up text with 100 valid phone numbers.
string numbers;
for (int i = 0; i < 100; ++i) {
numbers.append("My info: 415-666-7777,");
}
// Matches all 100. Max only applies to failed cases.
PhoneNumber number;
phone_util_.Parse("+14156667777", RegionCode::US(), &number);
vector<PhoneNumber> expected(100, number);
PhoneNumberMatcher matcher(
phone_util_, numbers, RegionCode::US(), PhoneNumberMatcher::VALID, 10);
vector<PhoneNumber> actual;
PhoneNumberMatch match;
while (matcher.HasNext()) {
matcher.Next(&match);
actual.push_back(match.number());
}
EXPECT_EQ(expected, actual);
}
TEST_F(PhoneNumberMatcherTest, MaxMatchesInvalid) {
// Set up text with 10 invalid phone numbers followed by 100 valid.
string numbers;
for (int i = 0; i < 10; ++i) {
numbers.append("My address 949-8945-0");
}
for (int i = 0; i < 100; ++i) {
numbers.append("My info: 415-666-7777,");
}
PhoneNumberMatcher matcher(
phone_util_, numbers, RegionCode::US(), PhoneNumberMatcher::VALID, 10);
EXPECT_FALSE(matcher.HasNext());
}
TEST_F(PhoneNumberMatcherTest, MaxMatchesMixed) {
// Set up text with 100 valid numbers inside an invalid number.
string numbers;
for (int i = 0; i < 100; ++i) {
numbers.append("My info: 415-666-7777 123 fake street");
}
PhoneNumber number;
phone_util_.Parse("+14156667777", RegionCode::ZZ(), &number);
vector<PhoneNumber> expected(10, number);
PhoneNumberMatcher matcher(
phone_util_, numbers, RegionCode::US(), PhoneNumberMatcher::VALID, 10);
vector<PhoneNumber> actual;
PhoneNumberMatch match;
while (matcher.HasNext()) {
matcher.Next(&match);
actual.push_back(match.number());
}
EXPECT_EQ(expected, actual);
}
TEST_F(PhoneNumberMatcherTest, NonPlusPrefixedNumbersNotFoundForInvalidRegion) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("1 456 764 156", RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
EXPECT_FALSE(matcher->Next(&match));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, EmptyIteration) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("", RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
EXPECT_FALSE(matcher->HasNext());
EXPECT_FALSE(matcher->HasNext());
EXPECT_FALSE(matcher->Next(&match));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, SingleIteration) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("+14156667777", RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
// Try HasNext() twice to ensure it does not advance.
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->Next(&match));
EXPECT_FALSE(matcher->HasNext());
EXPECT_FALSE(matcher->Next(&match));
}
TEST_F(PhoneNumberMatcherTest, SingleIteration_WithNextOnly) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("+14156667777", RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
EXPECT_TRUE(matcher->Next(&match));
EXPECT_FALSE(matcher->Next(&match));
}
TEST_F(PhoneNumberMatcherTest, DoubleIteration) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("+14156667777 foobar +14156667777 ",
RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
// Double HasNext() to ensure it does not advance.
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->Next(&match));
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->HasNext());
EXPECT_TRUE(matcher->Next(&match));
EXPECT_FALSE(matcher->HasNext());
EXPECT_FALSE(matcher->Next(&match));
EXPECT_FALSE(matcher->HasNext());
}
TEST_F(PhoneNumberMatcherTest, DoubleIteration_WithNextOnly) {
PhoneNumberMatch match;
scoped_ptr<PhoneNumberMatcher> matcher(
GetMatcherWithLeniency("+14156667777 foobar +14156667777 ",
RegionCode::GetUnknown(),
PhoneNumberMatcher::VALID));
EXPECT_TRUE(matcher->Next(&match));
EXPECT_TRUE(matcher->Next(&match));
EXPECT_FALSE(matcher->Next(&match));
}
} // namespace phonenumbers
} // namespace i18n