Browse Source

CPP: Add AsYouTypeFormatter (port from Java).

pull/567/head
Philippe Liard 14 years ago
committed by Mihaela Rosca
parent
commit
4bffe68fe1
11 changed files with 2138 additions and 12 deletions
  1. +12
    -0
      cpp/CMakeLists.txt
  2. +11
    -10
      cpp/src/base/logging.h
  3. +629
    -0
      cpp/src/phonenumbers/asyoutypeformatter.cc
  4. +197
    -0
      cpp/src/phonenumbers/asyoutypeformatter.h
  5. +24
    -0
      cpp/src/phonenumbers/phonenumberutil.cc
  6. +15
    -0
      cpp/src/phonenumbers/phonenumberutil.h
  7. +114
    -0
      cpp/src/phonenumbers/unicodestring.cc
  8. +133
    -0
      cpp/src/phonenumbers/unicodestring.h
  9. +759
    -0
      cpp/test/phonenumbers/asyoutypeformatter_test.cc
  10. +236
    -0
      cpp/test/phonenumbers/unicodestring_test.cc
  11. +8
    -2
      tools/script/continuous-integration.sh

+ 12
- 0
cpp/CMakeLists.txt View File

@ -153,6 +153,7 @@ add_custom_target (
set (
SOURCES
"src/base/string_piece.cc"
"src/phonenumbers/asyoutypeformatter.cc"
"src/phonenumbers/default_logger.cc"
"src/phonenumbers/logger.cc"
"src/phonenumbers/metadata.h" # Generated by build tools.
@ -162,6 +163,7 @@ set (
"src/phonenumbers/phonenumberutil.cc"
"src/phonenumbers/regexp_cache.cc"
"src/phonenumbers/stringutil.cc"
"src/phonenumbers/unicodestring.cc"
"src/phonenumbers/utf/rune.c"
"src/phonenumbers/utf/unicodetext.cc"
"src/phonenumbers/utf/unilib.cc"
@ -291,12 +293,14 @@ target_link_libraries (phonenumber_testing ${LIBRARY_DEPS})
add_dependencies (phonenumber_testing generate-sources ${TEST_METADATA_TARGET})
set (TEST_SOURCES
"test/phonenumbers/asyoutypeformatter_test.cc"
"test/phonenumbers/logger_test.cc"
"test/phonenumbers/phonenumberutil_test.cc"
"test/phonenumbers/regexp_adapter_test.cc"
"test/phonenumbers/regexp_cache_test.cc"
"test/phonenumbers/run_tests.cc"
"test/phonenumbers/stringutil_test.cc"
"test/phonenumbers/unicodestring_test.cc"
"test/phonenumbers/utf/unicodetext_test.cc"
)
@ -311,12 +315,20 @@ add_custom_target(test COMMAND libphonenumber_test DEPENDS libphonenumber_test)
# Install rules.
install (FILES
"src/phonenumbers/asyoutypeformatter.h"
"src/phonenumbers/logger.h"
"src/phonenumbers/phonenumber.pb.h"
"src/phonenumbers/phonemetadata.pb.h"
"src/phonenumbers/phonenumberutil.h"
"src/phonenumbers/regexp_adapter.h"
"src/phonenumbers/regexp_cache.h"
"src/phonenumbers/unicodestring.h"
DESTINATION include/phonenumbers/
)
install (FILES "src/phonenumbers/utf/unicodetext.h"
DESTINATION include/phonenumbers/utf/)
install (FILES src/base/basictypes.h
DESTINATION include/base/)


+ 11
- 10
cpp/src/base/logging.h View File

@ -14,20 +14,21 @@
// Author: Philippe Liard
// This file does not come from Chromium.
// It provides a minimalist implementation of common macros.
// This file provides a minimalist implementation of common macros.
#ifndef BASE_LOGGING_H_
# define BASE_LOGGING_H_
#define BASE_LOGGING_H_
# include <cassert>
# include <iostream>
#include <cassert>
# define CHECK_EQ(X, Y) assert((X) == (Y))
#define CHECK_EQ(X, Y) assert((X) == (Y))
# define DCHECK(X) assert(X)
# define DCHECK_EQ(X, Y) CHECK_EQ((X), (Y))
#define DCHECK(X) assert(X)
#define DCHECK_EQ(X, Y) CHECK_EQ((X), (Y))
# define NOTREACHED() std::cerr
template <typename T> T* CHECK_NOTNULL(T* ptr) {
assert(ptr);
return ptr;
}
#endif
#endif // BASE_LOGGING_H_

+ 629
- 0
cpp/src/phonenumbers/asyoutypeformatter.cc View File

@ -0,0 +1,629 @@
// 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.
#include "phonenumbers/asyoutypeformatter.h"
#include <cctype>
#include <list>
#include <string>
#include <google/protobuf/message_lite.h>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "phonenumbers/phonemetadata.pb.h"
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/regexp_cache.h"
#include "phonenumbers/regexp_factory.h"
#include "phonenumbers/stringutil.h"
#include "phonenumbers/unicodestring.h"
namespace i18n {
namespace phonenumbers {
using google::protobuf::RepeatedPtrField;
namespace {
const char kPlusSign = '+';
// A pattern that is used to match character classes in regular expressions.
// An example of a character class is [1-4].
const char kCharacterClassPattern[] = "\\[([^\\[\\]])*\\]";
// This is the minimum length of national number accrued that is required to
// trigger the formatter. The first element of the leading_digits_pattern of
// each number_format contains a regular expression that matches up to this
// number of digits.
const size_t kMinLeadingDigitsLength = 3;
// The digits that have not been entered yet will be represented by a \u2008,
// the punctuation space.
const char kDigitPlaceholder[] = "\xE2\x80\x88"; /* " " */
// Replaces any standalone digit in the pattern (not any inside a {} grouping)
// with \d. This function replaces the standalone digit regex used in the Java
// version which is currently not supported by RE2 because it uses a special
// construct (?=).
void ReplacePatternDigits(string* pattern) {
DCHECK(pattern);
string new_pattern;
for (string::const_iterator it = pattern->begin(); it != pattern->end();
++it) {
const char current_char = *it;
if (isdigit(current_char)) {
if (it + 1 != pattern->end()) {
const char next_char = it[1];
if (next_char != ',' && next_char != '}') {
new_pattern += "\\d";
} else {
new_pattern += current_char;
}
} else {
new_pattern += "\\d";
}
} else {
new_pattern += current_char;
}
}
pattern->assign(new_pattern);
}
// Matches all the groups contained in 'input' against 'pattern'.
void MatchAllGroups(const string& pattern,
const string& input,
const AbstractRegExpFactory& regexp_factory,
RegExpCache* cache,
string* group) {
DCHECK(cache);
DCHECK(group);
string new_pattern(pattern);
// Transforms pattern "(...)(...)(...)" to "(.........)".
strrmm(&new_pattern, "()");
new_pattern = StrCat("(", new_pattern, ")");
const scoped_ptr<RegExpInput> consume_input(
regexp_factory.CreateInput(input));
bool status =
cache->GetRegExp(new_pattern).Consume(consume_input.get(), group);
DCHECK(status);
}
PhoneMetadata CreateEmptyMetadata() {
PhoneMetadata metadata;
metadata.set_international_prefix("NA");
return metadata;
}
} // namespace
AsYouTypeFormatter::AsYouTypeFormatter(const string& region_code)
: regexp_factory_(new RegExpFactory()),
regexp_cache_(*regexp_factory_.get(), 64),
current_output_(),
formatting_template_(),
current_formatting_pattern_(),
accrued_input_(),
accrued_input_without_formatting_(),
able_to_format_(true),
is_international_formatting_(false),
is_expecting_country_code_(false),
phone_util_(*PhoneNumberUtil::GetInstance()),
default_country_(region_code),
empty_metadata_(CreateEmptyMetadata()),
default_metadata_(GetMetadataForRegion(region_code)),
current_metadata_(default_metadata_),
last_match_position_(0),
original_position_(0),
position_to_remember_(0),
prefix_before_national_number_(),
national_number_(),
possible_formats_() {
}
// The metadata needed by this class is the same for all regions sharing the
// same country calling code. Therefore, we return the metadata for "main"
// region for this country calling code.
const PhoneMetadata* AsYouTypeFormatter::GetMetadataForRegion(
const string& region_code) const {
int country_calling_code = phone_util_.GetCountryCodeForRegion(region_code);
string main_country;
phone_util_.GetRegionCodeForCountryCode(country_calling_code, &main_country);
const PhoneMetadata* const metadata =
phone_util_.GetMetadataForRegion(main_country);
if (metadata) {
return metadata;
}
// Set to a default instance of the metadata. This allows us to function with
// an incorrect region code, even if formatting only works for numbers
// specified with "+".
return &empty_metadata_;
}
bool AsYouTypeFormatter::MaybeCreateNewTemplate() {
// When there are multiple available formats, the formatter uses the first
// format where a formatting template could be created.
for (list<const NumberFormat*>::const_iterator it = possible_formats_.begin();
it != possible_formats_.end(); ++it) {
DCHECK(*it);
const NumberFormat& number_format = **it;
const string& pattern = number_format.pattern();
if (current_formatting_pattern_ == pattern) {
return false;
}
if (CreateFormattingTemplate(number_format)) {
current_formatting_pattern_ = pattern;
return true;
}
}
able_to_format_ = false;
return false;
}
void AsYouTypeFormatter::GetAvailableFormats(
const string& leading_three_digits) {
const RepeatedPtrField<NumberFormat>& format_list =
(is_international_formatting_ &&
current_metadata_->intl_number_format().size() > 0)
? current_metadata_->intl_number_format()
: current_metadata_->number_format();
for (RepeatedPtrField<NumberFormat>::const_iterator it = format_list.begin();
it != format_list.end(); ++it) {
if (phone_util_.IsFormatEligibleForAsYouTypeFormatter(it->format())) {
possible_formats_.push_back(&*it);
}
}
NarrowDownPossibleFormats(leading_three_digits);
}
void AsYouTypeFormatter::NarrowDownPossibleFormats(
const string& leading_digits) {
const int index_of_leading_digits_pattern =
leading_digits.length() - kMinLeadingDigitsLength;
for (list<const NumberFormat*>::iterator it = possible_formats_.begin();
it != possible_formats_.end(); ) {
DCHECK(*it);
const NumberFormat& format = **it;
if (format.leading_digits_pattern_size() >
index_of_leading_digits_pattern) {
const scoped_ptr<RegExpInput> input(
regexp_factory_->CreateInput(leading_digits));
if (!regexp_cache_.GetRegExp(format.leading_digits_pattern().Get(
index_of_leading_digits_pattern)).Consume(input.get())) {
it = possible_formats_.erase(it);
continue;
}
} // else the particular format has no more specific leadingDigitsPattern,
// and it should be retained.
++it;
}
}
bool AsYouTypeFormatter::CreateFormattingTemplate(const NumberFormat& format) {
string number_pattern = format.pattern();
// The formatter doesn't format numbers when numberPattern contains "|", e.g.
// (20|3)\d{4}. In those cases we quickly return.
if (number_pattern.find('|') != string::npos) {
return false;
}
// Replace anything in the form of [..] with \d.
static const scoped_ptr<const RegExp> character_class_pattern(
regexp_factory_->CreateRegExp(kCharacterClassPattern));
character_class_pattern->GlobalReplace(&number_pattern, "\\\\d");
// Replace any standalone digit (not the one in d{}) with \d.
ReplacePatternDigits(&number_pattern);
string number_format = format.format();
formatting_template_.remove();
UnicodeString temp_template;
GetFormattingTemplate(number_pattern, number_format, &temp_template);
if (temp_template.length() > 0) {
formatting_template_.append(temp_template);
return true;
}
return false;
}
void AsYouTypeFormatter::GetFormattingTemplate(
const string& number_pattern,
const string& number_format,
UnicodeString* formatting_template) {
DCHECK(formatting_template);
// Creates a phone number consisting only of the digit 9 that matches the
// number_pattern by applying the pattern to the longest_phone_number string.
static const char longest_phone_number[] = "999999999999999";
string a_phone_number;
MatchAllGroups(number_pattern, longest_phone_number, *regexp_factory_,
&regexp_cache_, &a_phone_number);
// No formatting template can be created if the number of digits entered so
// far is longer than the maximum the current formatting rule can accommodate.
if (a_phone_number.length() < national_number_.length()) {
formatting_template->remove();
return;
}
// Formats the number according to number_format.
regexp_cache_.GetRegExp(number_pattern).GlobalReplace(
&a_phone_number, number_format);
// Replaces each digit with character kDigitPlaceholder.
GlobalReplaceSubstring("9", kDigitPlaceholder, &a_phone_number);
formatting_template->setTo(a_phone_number.c_str(), a_phone_number.size());
}
void AsYouTypeFormatter::Clear() {
current_output_.clear();
accrued_input_.remove();
accrued_input_without_formatting_.remove();
formatting_template_.remove();
last_match_position_ = 0;
current_formatting_pattern_.clear();
prefix_before_national_number_.clear();
national_number_.clear();
able_to_format_ = true;
position_to_remember_ = 0;
original_position_ = 0;
is_international_formatting_ = false;
is_expecting_country_code_ = false;
possible_formats_.clear();
if (current_metadata_ != default_metadata_) {
current_metadata_ = GetMetadataForRegion(default_country_);
}
}
const string& AsYouTypeFormatter::InputDigit(char32 next_char, string* result) {
DCHECK(result);
InputDigitWithOptionToRememberPosition(next_char, false, &current_output_);
result->assign(current_output_);
return *result;
}
const string& AsYouTypeFormatter::InputDigitAndRememberPosition(
char32 next_char,
string* result) {
DCHECK(result);
InputDigitWithOptionToRememberPosition(next_char, true, &current_output_);
result->assign(current_output_);
return *result;
}
void AsYouTypeFormatter::InputDigitWithOptionToRememberPosition(
char32 next_char,
bool remember_position,
string* phone_number) {
DCHECK(phone_number);
accrued_input_.append(next_char);
if (remember_position) {
original_position_ = accrued_input_.length();
}
// We do formatting on-the-fly only when each character entered is either a
// plus sign or a digit.
string next_char_string;
UnicodeString(next_char).toUTF8String(next_char_string);
if (next_char != kPlusSign &&
!phone_util_.ContainsOnlyValidDigits(next_char_string)) {
able_to_format_ = false;
}
if (!able_to_format_) {
phone_number->clear();
accrued_input_.toUTF8String(*phone_number);
return;
}
char normalized_next_char =
NormalizeAndAccrueDigitsAndPlusSign(next_char, remember_position);
// We start to attempt to format only when at least kMinLeadingDigitsLength
// digits (the plus sign is counted as a digit as well for this purpose) have
// been entered.
switch (accrued_input_without_formatting_.length()) {
case 0:
case 1:
case 2:
phone_number->clear();
accrued_input_.toUTF8String(*phone_number);
return;
case 3:
if (AttemptToExtractIdd()) {
is_expecting_country_code_ = true;
} else {
// No IDD or plus sign is found, must be entering in national format.
RemoveNationalPrefixFromNationalNumber();
AttemptToChooseFormattingPattern(phone_number);
return;
}
case 4:
case 5:
if (is_expecting_country_code_) {
if (AttemptToExtractCountryCode()) {
is_expecting_country_code_ = false;
}
phone_number->assign(prefix_before_national_number_);
phone_number->append(national_number_);
return;
}
// We make a last attempt to extract a country code at the 6th digit because
// the maximum length of IDD and country code are both 3.
case 6:
if (is_expecting_country_code_ && !AttemptToExtractCountryCode()) {
able_to_format_ = false;
phone_number->clear();
accrued_input_.toUTF8String(*phone_number);
return;
}
default:
if (possible_formats_.size() > 0) {
// The formatting pattern is already chosen.
string temp_national_number;
InputDigitHelper(normalized_next_char, &temp_national_number);
// See if accrued digits can be formatted properly already. If not, use
// the results from InputDigitHelper, which does formatting based on the
// formatting pattern chosen.
string formatted_number;
AttemptToFormatAccruedDigits(&formatted_number);
if (formatted_number.length() > 0) {
phone_number->assign(formatted_number);
return;
}
NarrowDownPossibleFormats(national_number_);
if (MaybeCreateNewTemplate()) {
InputAccruedNationalNumber(phone_number);
return;
}
phone_number->assign(able_to_format_
? prefix_before_national_number_ + temp_national_number
: temp_national_number);
} else {
AttemptToChooseFormattingPattern(phone_number);
}
}
}
void AsYouTypeFormatter::AttemptToFormatAccruedDigits(
string* formatted_number) {
DCHECK(formatted_number);
for (list<const NumberFormat*>::const_iterator it = possible_formats_.begin();
it != possible_formats_.end(); ++it) {
DCHECK(*it);
const NumberFormat& num_format = **it;
string pattern = num_format.pattern();
if (regexp_cache_.GetRegExp(pattern).FullMatch(national_number_)) {
formatted_number->assign(national_number_);
string new_formatted_number(*formatted_number);
string format = num_format.format();
bool status = regexp_cache_.GetRegExp(pattern).GlobalReplace(
&new_formatted_number, format);
DCHECK(status);
formatted_number->assign(prefix_before_national_number_);
formatted_number->append(new_formatted_number);
return;
}
}
formatted_number->clear();
}
int AsYouTypeFormatter::GetRememberedPosition() const {
UnicodeString current_output(current_output_.c_str());
if (!able_to_format_) {
return ConvertUnicodeStringPosition(current_output, original_position_);
}
int accrued_input_index = 0;
int current_output_index = 0;
while (accrued_input_index < position_to_remember_ &&
current_output_index < current_output.length()) {
if (accrued_input_without_formatting_[accrued_input_index] ==
current_output[current_output_index]) {
++accrued_input_index;
}
++current_output_index;
}
return ConvertUnicodeStringPosition(current_output, current_output_index);
}
void AsYouTypeFormatter::AttemptToChooseFormattingPattern(
string* formatted_number) {
DCHECK(formatted_number);
if (national_number_.length() >= kMinLeadingDigitsLength) {
const string leading_digits =
national_number_.substr(0, kMinLeadingDigitsLength);
GetAvailableFormats(leading_digits);
MaybeCreateNewTemplate();
InputAccruedNationalNumber(formatted_number);
} else {
formatted_number->assign(prefix_before_national_number_ + national_number_);
}
}
void AsYouTypeFormatter::InputAccruedNationalNumber(string* number) {
DCHECK(number);
int length_of_national_number = national_number_.length();
if (length_of_national_number > 0) {
string temp_national_number;
for (int i = 0; i < length_of_national_number; ++i) {
temp_national_number.clear();
InputDigitHelper(national_number_[i], &temp_national_number);
}
number->assign(able_to_format_
? prefix_before_national_number_ + temp_national_number
: temp_national_number);
} else {
number->assign(prefix_before_national_number_);
}
}
void AsYouTypeFormatter::RemoveNationalPrefixFromNationalNumber() {
int start_of_national_number = 0;
if (current_metadata_->country_code() == 1 && national_number_[0] == '1') {
start_of_national_number = 1;
prefix_before_national_number_.append("1 ");
is_international_formatting_ = true;
} else if (current_metadata_->has_national_prefix()) {
const scoped_ptr<RegExpInput> consumed_input(
regexp_factory_->CreateInput(national_number_));
const RegExp& pattern = regexp_cache_.GetRegExp(
current_metadata_->national_prefix_for_parsing());
if (pattern.Consume(consumed_input.get())) {
// When the national prefix is detected, we use international formatting
// rules instead of national ones, because national formatting rules could
// countain local formatting rules for numbers entered without area code.
is_international_formatting_ = true;
start_of_national_number =
national_number_.length() - consumed_input->ToString().length();
prefix_before_national_number_.append(
national_number_.substr(0, start_of_national_number));
}
}
national_number_.erase(0, start_of_national_number);
}
bool AsYouTypeFormatter::AttemptToExtractIdd() {
string accrued_input_without_formatting_stdstring;
accrued_input_without_formatting_
.toUTF8String(accrued_input_without_formatting_stdstring);
const scoped_ptr<RegExpInput> consumed_input(
regexp_factory_->CreateInput(accrued_input_without_formatting_stdstring));
const RegExp& international_prefix = regexp_cache_.GetRegExp(
StrCat("\\", string(&kPlusSign, 1), "|",
current_metadata_->international_prefix()));
if (international_prefix.Consume(consumed_input.get())) {
is_international_formatting_ = true;
const int start_of_country_code =
accrued_input_without_formatting_.length() -
consumed_input->ToString().length();
national_number_.clear();
accrued_input_without_formatting_.tempSubString(start_of_country_code)
.toUTF8String(national_number_);
string before_country_code;
accrued_input_without_formatting_.tempSubString(0, start_of_country_code)
.toUTF8String(before_country_code);
prefix_before_national_number_.append(before_country_code);
if (accrued_input_without_formatting_[0] != kPlusSign) {
prefix_before_national_number_.append(" ");
}
return true;
}
return false;
}
bool AsYouTypeFormatter::AttemptToExtractCountryCode() {
if (national_number_.length() == 0) {
return false;
}
string number_without_country_code(national_number_);
int country_code =
phone_util_.ExtractCountryCode(&number_without_country_code);
if (country_code == 0) {
return false;
}
national_number_.assign(number_without_country_code);
string new_region_code;
phone_util_.GetRegionCodeForCountryCode(country_code, &new_region_code);
if (new_region_code != default_country_) {
current_metadata_ = GetMetadataForRegion(new_region_code);
}
StrAppend(&prefix_before_national_number_, country_code, " ");
return true;
}
char AsYouTypeFormatter::NormalizeAndAccrueDigitsAndPlusSign(
char32 next_char,
bool remember_position) {
char normalized_char = next_char;
if (next_char == kPlusSign) {
accrued_input_without_formatting_.append(next_char);
} else {
string number;
UnicodeString(next_char).toUTF8String(number);
phone_util_.NormalizeDigitsOnly(&number);
accrued_input_without_formatting_.append(next_char);
national_number_.append(number);
normalized_char = number[0];
}
if (remember_position) {
position_to_remember_ = accrued_input_without_formatting_.length();
}
return normalized_char;
}
void AsYouTypeFormatter::InputDigitHelper(char next_char, string* number) {
DCHECK(number);
number->clear();
const char32 placeholder_codepoint = UnicodeString(kDigitPlaceholder)[0];
int placeholder_pos = formatting_template_
.tempSubString(last_match_position_).indexOf(placeholder_codepoint);
if (placeholder_pos != -1) {
UnicodeString temp_template = formatting_template_;
placeholder_pos = temp_template.indexOf(placeholder_codepoint);
temp_template.setCharAt(placeholder_pos, UnicodeString(next_char)[0]);
last_match_position_ = placeholder_pos;
formatting_template_.replace(0, temp_template.length(), temp_template);
formatting_template_.tempSubString(0, last_match_position_ + 1)
.toUTF8String(*number);
} else {
if (possible_formats_.size() == 1) {
// More digits are entered than we could handle, and there are no other
// valid patterns to try.
able_to_format_ = false;
} // else, we just reset the formatting pattern.
current_formatting_pattern_.clear();
accrued_input_.toUTF8String(*number);
}
}
// Returns the number of bytes contained in the given UnicodeString up to the
// specified position.
// static
int AsYouTypeFormatter::ConvertUnicodeStringPosition(const UnicodeString& s,
int pos) {
if (pos > s.length()) {
return -1;
}
string substring;
s.tempSubString(0, pos).toUTF8String(substring);
return substring.length();
}
} // namespace phonenumbers
} // namespace i18n

+ 197
- 0
cpp/src/phonenumbers/asyoutypeformatter.h View File

@ -0,0 +1,197 @@
// 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.
// A formatter which formats phone numbers as they are entered.
//
// An AsYouTypeFormatter can be created by invoking the GetAsYouTypeFormatter
// method of the PhoneNumberUtil. After that digits can be added by invoking the
// InputDigit method on the formatter instance, and the partially formatted
// phone number will be returned each time a digit is added. The Clear method
// can be invoked before a new number needs to be formatted.
//
// See AYTF_US, AYTF_GBFixedLine and AYTF_DE test functions in
// asyoutypeformatter_test.cc for more details on how the formatter is to be
// used.
//
// This is a direct port from AsYouTypeFormatter.java. Changes to this class
// should also happen to the Java version, whenever it makes sense.
//
// This class is NOT THREAD SAFE.
#ifndef I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_
#define I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_
#include <list>
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "phonenumbers/regexp_adapter.h"
#include "phonenumbers/regexp_cache.h"
#include "phonenumbers/phonemetadata.pb.h"
#include "phonenumbers/unicodestring.h"
namespace i18n {
namespace phonenumbers {
using std::list;
using std::string;
class PhoneNumberUtil;
class AsYouTypeFormatter {
public:
~AsYouTypeFormatter() {}
// Formats a phone number on-the-fly as each digit is entered.
// next_char is the most recently entered digit of a phone number. Formatting
// characters are allowed, but as soon as they are encountered this method
// formats the number as entered and not "as you type" anymore. Full width
// digits and Arabic-indic digits are allowed, and will be shown as they are.
// Returns the partially formatted phone number (which is a reference to the
// given string parameter for convenience).
const string& InputDigit(char32 next_char, string* result);
// Same as InputDigit, but remembers the position where next_char is inserted,
// so that it could be retrieved later by using GetRememberedPosition(). The
// remembered position will be automatically adjusted if additional formatting
// characters are later inserted/removed in front of next_char.
// Returns the partially formatted phone number (which is a reference to the
// given string parameter for convenience).
const string& InputDigitAndRememberPosition(char32 next_char, string* result);
// Returns the current position in the partially formatted phone number of the
// character which was previously passed in as the parameter of
// InputDigitAndRememberPosition().
int GetRememberedPosition() const;
// Clears the internal state of the formatter, so it could be reused.
void Clear();
private:
// Constructs an as-you-type formatter. Should be obtained from
// PhoneNumberUtil::GetAsYouTypeFormatter().
explicit AsYouTypeFormatter(const string& region_code);
// Returns the metadata corresponding to the given region code or empty
// metadata if it is unsupported.
const PhoneMetadata* GetMetadataForRegion(const string& region_code) const;
// Returns true if a new template is created as opposed to reusing the
// existing template.
bool MaybeCreateNewTemplate();
void GetAvailableFormats(const string& leading_three_digits);
void NarrowDownPossibleFormats(const string& leading_digits);
bool CreateFormattingTemplate(const NumberFormat& format);
// Gets a formatting template which could be used to efficiently format a
// partial number where digits are added one by one.
void GetFormattingTemplate(const string& number_pattern,
const string& number_format,
UnicodeString* formatting_template);
void InputDigitWithOptionToRememberPosition(char32 next_char,
bool remember_position,
string* phone_number);
void AttemptToFormatAccruedDigits(string* formatted_number);
// Attempts to set the formatting template and assigns the passed-in string
// parameter to the formatted version of the digits entered so far.
void AttemptToChooseFormattingPattern(string* formatted_number);
// Invokes InputDigitHelper on each digit of the national number accrued, and
// assigns the passed-in string parameter to a formatted string in the end.
void InputAccruedNationalNumber(string* number);
void RemoveNationalPrefixFromNationalNumber();
// Extracts IDD and plus sign to prefix_before_national_number_ when they are
// available, and places the remaining input into national_number_.
bool AttemptToExtractIdd();
// Extracts country code from the begining of national_number_ to
// prefix_before_national_number_ when they are available, and places the
// remaining input into national_number_.
// Returns true when a valid country code can be found.
bool AttemptToExtractCountryCode();
// Accrues digits and the plus sign to accrued_input_without_formatting for
// later use. If next_char contains a digit in non-ASCII format (e.g the
// full-width version of digits), it is first normalized to the ASCII
// version. The return value is next_char itself, or its normalized version,
// if next_char is a digit in non-ASCII format.
char NormalizeAndAccrueDigitsAndPlusSign(char32 next_char,
bool remember_position);
void InputDigitHelper(char next_char, string* number);
// Converts UnicodeString position to std::string position.
static int ConvertUnicodeStringPosition(const UnicodeString& s, int pos);
// Class attributes.
const scoped_ptr<const AbstractRegExpFactory> regexp_factory_;
RegExpCache regexp_cache_;
string current_output_;
UnicodeString formatting_template_;
string current_formatting_pattern_;
UnicodeString accrued_input_;
UnicodeString accrued_input_without_formatting_;
bool able_to_format_;
bool is_international_formatting_;
bool is_expecting_country_code_;
const PhoneNumberUtil& phone_util_;
const string default_country_;
const PhoneMetadata empty_metadata_;
const PhoneMetadata* const default_metadata_;
const PhoneMetadata* current_metadata_;
int last_match_position_;
// The position of a digit upon which InputDigitAndRememberPosition is most
// recently invoked, as found in the original sequence of characters the user
// entered.
int original_position_;
// The position of a digit upon which InputDigitAndRememberPosition is most
// recently invoked, as found in AccruedInputWithoutFormatting.
int position_to_remember_;
string prefix_before_national_number_;
string national_number_;
list<const NumberFormat*> possible_formats_;
friend class PhoneNumberUtil;
friend class AsYouTypeFormatterTest;
// Disallow copy and assign since this class uses RegExpCache which can't be
// copied.
DISALLOW_COPY_AND_ASSIGN(AsYouTypeFormatter);
};
} // namespace phonenumbers
} // namespace i18n
#endif // I18N_PHONENUMBERS_ASYOUTYPEFORMATTER_H_

+ 24
- 0
cpp/src/phonenumbers/phonenumberutil.cc View File

@ -34,6 +34,7 @@
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "phonenumbers/asyoutypeformatter.h"
#include "phonenumbers/default_logger.h"
#include "phonenumbers/encoding_utils.h"
#include "phonenumbers/metadata.h"
@ -748,6 +749,10 @@ const string& PhoneNumberUtil::GetExtnPatternsForMatching() const {
return *(extn_patterns_for_matching.get());
}
bool PhoneNumberUtil::ContainsOnlyValidDigits(const string& s) const {
return digits_pattern->FullMatch(s);
}
void PhoneNumberUtil::TrimUnwantedEndChars(string* number) const {
DCHECK(number);
UnicodeText number_as_unicode;
@ -767,6 +772,20 @@ void PhoneNumberUtil::TrimUnwantedEndChars(string* number) const {
reverse_it.base()));
}
bool PhoneNumberUtil::IsFormatEligibleForAsYouTypeFormatter(
const string& format) const {
// A pattern that is used to determine if a numberFormat under
// availableFormats is eligible to be used by the AYTF. It is eligible when
// the format element under numberFormat contains groups of the dollar sign
// followed by a single digit, separated by valid phone number punctuation.
// This prevents invalid punctuation (such as the star sign in Israeli star
// numbers) getting into the output of the AYTF.
const RegExp& eligible_format_pattern = regexp_cache->GetRegExp(
StrCat("[", kValidPunctuation, "]*", "(\\$\\d", "[",
kValidPunctuation, "]*)+"));
return eligible_format_pattern.FullMatch(format);
}
void PhoneNumberUtil::GetSupportedRegions(set<string>* regions) const {
DCHECK(regions);
for (map<string, PhoneMetadata>::const_iterator it =
@ -2207,5 +2226,10 @@ PhoneNumberUtil::MatchType PhoneNumberUtil::IsNumberMatchWithOneString(
return INVALID_NUMBER;
}
AsYouTypeFormatter* PhoneNumberUtil::GetAsYouTypeFormatter(
const string& region_code) const {
return new AsYouTypeFormatter(region_code);
}
} // namespace phonenumbers
} // namespace i18n

+ 15
- 0
cpp/src/phonenumbers/phonenumberutil.h View File

@ -46,6 +46,7 @@ using std::vector;
using google::protobuf::RepeatedPtrField;
class AsYouTypeFormatter;
class Logger;
class NumberFormat;
class PhoneMetadata;
@ -65,6 +66,7 @@ class PhoneNumberUtil {
class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
friend class Singleton<PhoneNumberUtil>;
#endif
friend class AsYouTypeFormatter;
friend class PhoneNumberUtilTest;
public:
~PhoneNumberUtil();
@ -499,6 +501,13 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// handled by this class (i.e don't delete it).
static void SetLogger(Logger* logger);
// Gets an AsYouTypeFormatter for the specific region.
// Returns an AsYouTypeFormatter object, which could be used to format phone
// numbers in the specific region "as you type".
// The deletion of the returned instance is under the responsibility of the
// caller.
AsYouTypeFormatter* GetAsYouTypeFormatter(const string& region_code) const;
friend bool ConvertFromTelephoneNumberProto(
const TelephoneNumber& proto_to_convert,
PhoneNumber* new_proto);
@ -560,6 +569,12 @@ class PhoneNumberUtil : public Singleton<PhoneNumberUtil> {
// in a number, for use when matching.
const string& GetExtnPatternsForMatching() const;
// Checks whether a string contains only valid digits.
bool ContainsOnlyValidDigits(const string& s) const;
// Checks if a format is eligible to be used by the AsYouTypeFormatter.
bool IsFormatEligibleForAsYouTypeFormatter(const string& format) const;
// Trims unwanted end characters from a phone number string.
void TrimUnwantedEndChars(string* number) const;


+ 114
- 0
cpp/src/phonenumbers/unicodestring.cc View File

@ -0,0 +1,114 @@
// 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: Philippe Liard
#include "phonenumbers/unicodestring.h"
#include <algorithm>
#include <cassert>
#include <iterator>
using std::advance;
using std::equal;
namespace i18n {
namespace phonenumbers {
UnicodeString& UnicodeString::operator=(const UnicodeString& src) {
if (&src != this) {
invalidateCachedIndex();
text_ = src.text_;
}
return *this;
}
bool UnicodeString::operator==(const UnicodeString& rhs) const {
return equal(text_.begin(), text_.end(), rhs.text_.begin());
}
void UnicodeString::append(const UnicodeString& unicode_string) {
invalidateCachedIndex();
for (UnicodeString::const_iterator it = unicode_string.begin();
it != unicode_string.end(); ++it) {
append(*it);
}
}
int UnicodeString::indexOf(char32 codepoint) const {
int pos = 0;
for (UnicodeText::const_iterator it = text_.begin(); it != text_.end();
++it, ++pos) {
if (*it == codepoint) {
return pos;
}
}
return -1;
}
void UnicodeString::replace(int start, int length, const UnicodeString& src) {
assert(length >= 0 && length <= this->length());
invalidateCachedIndex();
UnicodeText::const_iterator start_it = text_.begin();
advance(start_it, start);
UnicodeText unicode_text;
unicode_text.append(text_.begin(), start_it);
unicode_text.append(src.text_);
advance(start_it, length);
unicode_text.append(start_it, text_.end());
text_ = unicode_text;
}
void UnicodeString::setCharAt(int pos, char32 c) {
assert(pos < length());
invalidateCachedIndex();
UnicodeText::const_iterator pos_it = text_.begin();
advance(pos_it, pos);
UnicodeText unicode_text;
unicode_text.append(text_.begin(), pos_it);
unicode_text.push_back(c);
++pos_it;
unicode_text.append(pos_it, text_.end());
text_ = unicode_text;
}
UnicodeString UnicodeString::tempSubString(int start, int length) const {
const int unicodestring_length = this->length();
if (length == std::numeric_limits<int>::max()) {
length = unicodestring_length - start;
}
if (start > unicodestring_length || length > unicodestring_length) {
return UnicodeString("");
}
UnicodeText::const_iterator start_it = text_.begin();
advance(start_it, start);
UnicodeText::const_iterator end_it = start_it;
advance(end_it, length);
UnicodeString substring;
substring.text_.PointTo(start_it, end_it);
return substring;
}
char32 UnicodeString::operator[](int index) const {
assert(index < length());
if (cached_index_ == -1 || cached_index_ > index) {
cached_it_ = text_.begin();
cached_index_ = 0;
}
for (; cached_index_ < index; ++cached_index_, ++cached_it_) {}
return *cached_it_;
}
} // namespace phonenumbers
} // namespace i18n

+ 133
- 0
cpp/src/phonenumbers/unicodestring.h View File

@ -0,0 +1,133 @@
// 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: Philippe Liard
#ifndef I18N_PHONENUMBERS_UNICODESTRING_H_
#define I18N_PHONENUMBERS_UNICODESTRING_H_
#include "phonenumbers/utf/unicodetext.h"
#include <cstring>
#include <limits>
namespace i18n {
namespace phonenumbers {
// This class supports the minimal subset of icu::UnicodeString needed by
// AsYouTypeFormatter in order to let the libphonenumber not depend on ICU
// which is not available by default on some systems, such as iOS.
class UnicodeString {
public:
UnicodeString() : cached_index_(-1) {}
// Constructs a new unicode string copying the provided C string.
explicit UnicodeString(const char* utf8)
: text_(UTF8ToUnicodeText(utf8, std::strlen(utf8))),
cached_index_(-1) {}
// Constructs a new unicode string containing the provided codepoint.
explicit UnicodeString(char32 codepoint) : cached_index_(-1) {
append(codepoint);
}
UnicodeString(const UnicodeString& src)
: text_(src.text_), cached_index_(-1) {}
UnicodeString& operator=(const UnicodeString& src);
bool operator==(const UnicodeString& rhs) const;
void append(const UnicodeString& unicode_string);
inline void append(char32 codepoint) {
invalidateCachedIndex();
text_.push_back(codepoint);
}
typedef UnicodeText::const_iterator const_iterator;
inline const_iterator begin() const {
return text_.begin();
}
inline const_iterator end() const {
return text_.end();
}
// Returns the index of the provided codepoint or -1 if not found.
int indexOf(char32 codepoint) const;
// Returns the number of codepoints contained in the unicode string.
inline int length() const {
return text_.size();
}
// Clears the unicode string.
inline void remove() {
invalidateCachedIndex();
text_.clear();
}
// Replaces the substring located at [ start, start + length - 1 ] with the
// provided unicode string.
void replace(int start, int length, const UnicodeString& src);
void setCharAt(int pos, char32 c);
// Copies the provided C string.
inline void setTo(const char* s, size_t len) {
invalidateCachedIndex();
text_.CopyUTF8(s, len);
}
// Returns the substring located at [ start, start + length - 1 ] without
// copying the underlying C string. If one of the provided parameters is out
// of range, the function returns an empty unicode string.
UnicodeString tempSubString(
int start,
int length = std::numeric_limits<int>::max()) const;
inline void toUTF8String(string& out) const {
out = UnicodeTextToUTF8(text_);
}
char32 operator[](int index) const;
private:
UnicodeText text_;
// As UnicodeText doesn't provide random access, an operator[] implementation
// would naively iterate from the beginning of the string to the supplied
// index which would be inefficient.
// As operator[] is very likely to be called in a loop with consecutive
// indexes, we save the corresponding iterator so we can reuse it the next
// time it is called.
// The following function which invalidates the cached index corresponding to
// the iterator position must be called every time the unicode string is
// modified (i.e. in all the non-const methods).
inline void invalidateCachedIndex() {
cached_index_ = -1;
}
// Iterator corresponding to the cached index below, used by operator[].
mutable UnicodeText::const_iterator cached_it_;
mutable int cached_index_;
};
} // namespace phonenumbers
} // namespace i18n
#endif // I18N_PHONENUMBERS_UNICODESTRING_H_

+ 759
- 0
cpp/test/phonenumbers/asyoutypeformatter_test.cc View File

@ -0,0 +1,759 @@
// 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.
// Unit tests for asyoutypeformatter.cc, ported from AsYouTypeFormatterTest.java
#include "phonenumbers/asyoutypeformatter.h"
#include <gtest/gtest.h>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "phonenumbers/phonenumberutil.h"
#include "phonenumbers/region_code.h"
namespace i18n {
namespace phonenumbers {
class PhoneMetadata;
class AsYouTypeFormatterTest : public testing::Test {
protected:
AsYouTypeFormatterTest() : phone_util_(*PhoneNumberUtil::GetInstance()) {
}
const PhoneMetadata* GetCurrentMetadata() const {
return CHECK_NOTNULL(formatter_.get())->current_metadata_;
}
int ConvertUnicodeStringPosition(const UnicodeString& s, int pos) const {
return AsYouTypeFormatter::ConvertUnicodeStringPosition(s, pos);
}
const PhoneNumberUtil& phone_util_;
scoped_ptr<AsYouTypeFormatter> formatter_;
string result_;
private:
DISALLOW_COPY_AND_ASSIGN(AsYouTypeFormatterTest);
};
TEST_F(AsYouTypeFormatterTest, ConvertUnicodeStringPosition) {
EXPECT_EQ(-1, ConvertUnicodeStringPosition(UnicodeString("12345"), 10));
EXPECT_EQ(3, ConvertUnicodeStringPosition(UnicodeString("12345"), 3));
EXPECT_EQ(0, ConvertUnicodeStringPosition(
UnicodeString("\xEF\xBC\x95" /* "5" */), 0));
EXPECT_EQ(4, ConvertUnicodeStringPosition(
UnicodeString("0\xEF\xBC\x95""3" /* "053" */), 2));
EXPECT_EQ(5, ConvertUnicodeStringPosition(
UnicodeString("0\xEF\xBC\x95""3" /* "053" */), 3));
}
TEST_F(AsYouTypeFormatterTest, Constructor) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_TRUE(GetCurrentMetadata() != NULL);
}
TEST_F(AsYouTypeFormatterTest, TooLongNumberMatchingMultipleLeadingDigits) {
// See http://code.google.com/p/libphonenumber/issues/detail?id=36
// The bug occurred last time for countries which have two formatting rules
// with exactly the same leading digits pattern but differ in length.
formatter_.reset(phone_util_.GetAsYouTypeFormatter(RegionCode::GetUnknown()));
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 9", formatter_->InputDigit('9', &result_));
EXPECT_EQ("+81 90", formatter_->InputDigit('0', &result_));
EXPECT_EQ("+81 90 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 90 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 90 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+81 90 1234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+81 90 1234 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+81 90 1234 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+81 90 1234 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+81 90 1234 5678", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 90 12 345 6789", formatter_->InputDigit('9', &result_));
EXPECT_EQ("+81901234567890", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_US) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_EQ("6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("650", formatter_->InputDigit('0', &result_));
EXPECT_EQ("650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("650 253", formatter_->InputDigit('3', &result_));
// Note this is how a US local number (without area code) should be formatted.
EXPECT_EQ("650 2532", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650 253 2222", formatter_->InputDigit('2', &result_));
formatter_->Clear();
EXPECT_EQ("1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("16", formatter_->InputDigit('6', &result_));
EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("1 650", formatter_->InputDigit('0', &result_));
EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_));
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 4", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011 44 ", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011 44 6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("011 44 61", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 44 6 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 44 6 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 44 6 123 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 44 6 123 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 44 6 123 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 44 6 123 123 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 44 6 123 123 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 44 6 123 123 123", formatter_->InputDigit('3', &result_));
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("011 54 ", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011 54 9", formatter_->InputDigit('9', &result_));
EXPECT_EQ("011 54 91", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 54 9 11", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 54 9 11 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 54 9 11 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 54 9 11 231", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 54 9 11 2312", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 54 9 11 2312 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 54 9 11 2312 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 54 9 11 2312 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 54 9 11 2312 1234", formatter_->InputDigit('4', &result_));
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 24", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011 244 ", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011 244 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 244 28", formatter_->InputDigit('8', &result_));
EXPECT_EQ("011 244 280", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 00", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 000", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 000 0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 000 00", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 244 280 000 000", formatter_->InputDigit('0', &result_));
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+4", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+48 ", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+48 8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+48 88", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+48 88 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+48 88 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+48 88 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+48 88 123 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+48 88 123 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+48 88 123 12 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+48 88 123 12 12", formatter_->InputDigit('2', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_USFullWidthCharacters) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_EQ("\xEF\xBC\x96" /* "6" */,
formatter_->InputDigit(UnicodeString("\xEF\xBC\x96" /* "6" */)[0],
&result_));
EXPECT_EQ("\xEF\xBC\x96\xEF\xBC\x95" /* "65" */,
formatter_->InputDigit(UnicodeString("\xEF\xBC\x95" /* "5" */)[0],
&result_));
EXPECT_EQ("650",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x90" /* "0" */)[0],
&result_));
EXPECT_EQ("650 2",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0],
&result_));
EXPECT_EQ("650 25",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x95" /* "5" */)[0],
&result_));
EXPECT_EQ("650 253",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x93" /* "3" */)[0],
&result_));
EXPECT_EQ("650 2532",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0],
&result_));
EXPECT_EQ("650 253 22",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0],
&result_));
EXPECT_EQ("650 253 222",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0],
&result_));
EXPECT_EQ("650 253 2222",
formatter_->InputDigit(UnicodeString("\xEF\xBC\x92" /* "2" */)[0],
&result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_USMobileShortCode) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_EQ("*", formatter_->InputDigit('*', &result_));
EXPECT_EQ("*1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("*12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("*121", formatter_->InputDigit('1', &result_));
EXPECT_EQ("*121#", formatter_->InputDigit('#', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_USVanityNumber) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_EQ("8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("80", formatter_->InputDigit('0', &result_));
EXPECT_EQ("800", formatter_->InputDigit('0', &result_));
EXPECT_EQ("800 ", formatter_->InputDigit(' ', &result_));
EXPECT_EQ("800 M", formatter_->InputDigit('M', &result_));
EXPECT_EQ("800 MY", formatter_->InputDigit('Y', &result_));
EXPECT_EQ("800 MY ", formatter_->InputDigit(' ', &result_));
EXPECT_EQ("800 MY A", formatter_->InputDigit('A', &result_));
EXPECT_EQ("800 MY AP", formatter_->InputDigit('P', &result_));
EXPECT_EQ("800 MY APP", formatter_->InputDigit('P', &result_));
EXPECT_EQ("800 MY APPL", formatter_->InputDigit('L', &result_));
EXPECT_EQ("800 MY APPLE", formatter_->InputDigit('E', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTFAndRememberPositionUS) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("US"));
EXPECT_EQ("1", formatter_->InputDigitAndRememberPosition('1', &result_));
EXPECT_EQ(1, formatter_->GetRememberedPosition());
EXPECT_EQ("16", formatter_->InputDigit('6', &result_));
EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ(1, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650", formatter_->InputDigitAndRememberPosition('0', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_));
// Note the remembered position for digit "0" changes from 4 to 5, because a
// space is now inserted in the front.
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 253 222", formatter_->InputDigitAndRememberPosition('2',
&result_));
EXPECT_EQ(13, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(13, formatter_->GetRememberedPosition());
EXPECT_EQ("165025322222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(10, formatter_->GetRememberedPosition());
EXPECT_EQ("1650253222222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(10, formatter_->GetRememberedPosition());
formatter_->Clear();
EXPECT_EQ("1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("16", formatter_->InputDigitAndRememberPosition('6', &result_));
EXPECT_EQ(2, formatter_->GetRememberedPosition());
EXPECT_EQ("1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("1 650", formatter_->InputDigit('0', &result_));
EXPECT_EQ(3, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ(3, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ(3, formatter_->GetRememberedPosition());
EXPECT_EQ("1 650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("1 650 253 2222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("165025322222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(2, formatter_->GetRememberedPosition());
EXPECT_EQ("1650253222222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(2, formatter_->GetRememberedPosition());
formatter_->Clear();
EXPECT_EQ("6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("650", formatter_->InputDigit('0', &result_));
EXPECT_EQ("650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("650 2532",
formatter_->InputDigitAndRememberPosition('2', &result_));
EXPECT_EQ(8, formatter_->GetRememberedPosition());
EXPECT_EQ("650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ(9, formatter_->GetRememberedPosition());
EXPECT_EQ("650 253 222", formatter_->InputDigit('2', &result_));
// No more formatting when semicolon is entered.
EXPECT_EQ("650253222;", formatter_->InputDigit(';', &result_));
EXPECT_EQ(7, formatter_->GetRememberedPosition());
EXPECT_EQ("650253222;2", formatter_->InputDigit('2', &result_));
formatter_->Clear();
EXPECT_EQ("6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("650", formatter_->InputDigit('0', &result_));
// No more formatting when users choose to do their own formatting.
EXPECT_EQ("650-", formatter_->InputDigit('-', &result_));
EXPECT_EQ("650-2", formatter_->InputDigitAndRememberPosition('2', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("650-25", formatter_->InputDigit('5', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("650-253", formatter_->InputDigit('3', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("650-253-", formatter_->InputDigit('-', &result_));
EXPECT_EQ("650-253-2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650-253-22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650-253-222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("650-253-2222", formatter_->InputDigit('2', &result_));
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 4", formatter_->InputDigitAndRememberPosition('4', &result_));
EXPECT_EQ("011 48 ", formatter_->InputDigit('8', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("011 48 8", formatter_->InputDigit('8', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("011 48 88", formatter_->InputDigit('8', &result_));
EXPECT_EQ("011 48 88 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 48 88 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("011 48 88 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 48 88 123 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 48 88 123 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("011 48 88 123 12 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 48 88 123 12 12", formatter_->InputDigit('2', &result_));
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+1 6", formatter_->InputDigitAndRememberPosition('6', &result_));
EXPECT_EQ("+1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+1 650", formatter_->InputDigit('0', &result_));
EXPECT_EQ(4, formatter_->GetRememberedPosition());
EXPECT_EQ("+1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ(4, formatter_->GetRememberedPosition());
EXPECT_EQ("+1 650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+1 650 253",
formatter_->InputDigitAndRememberPosition('3', &result_));
EXPECT_EQ("+1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+1 650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ(10, formatter_->GetRememberedPosition());
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+1 6", formatter_->InputDigitAndRememberPosition('6', &result_));
EXPECT_EQ("+1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+1 650", formatter_->InputDigit('0', &result_));
EXPECT_EQ(4, formatter_->GetRememberedPosition());
EXPECT_EQ("+1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ(4, formatter_->GetRememberedPosition());
EXPECT_EQ("+1 650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+1 650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+1 650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+1650253222;", formatter_->InputDigit(';', &result_));
EXPECT_EQ(3, formatter_->GetRememberedPosition());
}
TEST_F(AsYouTypeFormatterTest, AYTF_GBFixedLine) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("02", formatter_->InputDigit('2', &result_));
EXPECT_EQ("020", formatter_->InputDigit('0', &result_));
EXPECT_EQ("020 7", formatter_->InputDigitAndRememberPosition('7', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("020 70", formatter_->InputDigit('0', &result_));
EXPECT_EQ("020 703", formatter_->InputDigit('3', &result_));
EXPECT_EQ(5, formatter_->GetRememberedPosition());
EXPECT_EQ("020 7031", formatter_->InputDigit('1', &result_));
EXPECT_EQ("020 7031 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("020 7031 30", formatter_->InputDigit('0', &result_));
EXPECT_EQ("020 7031 300", formatter_->InputDigit('0', &result_));
EXPECT_EQ("020 7031 3000", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_GBTollFree) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("08", formatter_->InputDigit('8', &result_));
EXPECT_EQ("080", formatter_->InputDigit('0', &result_));
EXPECT_EQ("080 7", formatter_->InputDigit('7', &result_));
EXPECT_EQ("080 70", formatter_->InputDigit('0', &result_));
EXPECT_EQ("080 703", formatter_->InputDigit('3', &result_));
EXPECT_EQ("080 7031", formatter_->InputDigit('1', &result_));
EXPECT_EQ("080 7031 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("080 7031 30", formatter_->InputDigit('0', &result_));
EXPECT_EQ("080 7031 300", formatter_->InputDigit('0', &result_));
EXPECT_EQ("080 7031 3000", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_GBPremiumRate) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("GB"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("09", formatter_->InputDigit('9', &result_));
EXPECT_EQ("090", formatter_->InputDigit('0', &result_));
EXPECT_EQ("090 7", formatter_->InputDigit('7', &result_));
EXPECT_EQ("090 70", formatter_->InputDigit('0', &result_));
EXPECT_EQ("090 703", formatter_->InputDigit('3', &result_));
EXPECT_EQ("090 7031", formatter_->InputDigit('1', &result_));
EXPECT_EQ("090 7031 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("090 7031 30", formatter_->InputDigit('0', &result_));
EXPECT_EQ("090 7031 300", formatter_->InputDigit('0', &result_));
EXPECT_EQ("090 7031 3000", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_NZMobile) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("NZ"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("02", formatter_->InputDigit('2', &result_));
EXPECT_EQ("021", formatter_->InputDigit('1', &result_));
EXPECT_EQ("02-11", formatter_->InputDigit('1', &result_));
EXPECT_EQ("02-112", formatter_->InputDigit('2', &result_));
// Note the unittest is using fake metadata which might produce non-ideal
// results.
EXPECT_EQ("02-112 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("02-112 34", formatter_->InputDigit('4', &result_));
EXPECT_EQ("02-112 345", formatter_->InputDigit('5', &result_));
EXPECT_EQ("02-112 3456", formatter_->InputDigit('6', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_DE) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("DE"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("03", formatter_->InputDigit('3', &result_));
EXPECT_EQ("030", formatter_->InputDigit('0', &result_));
EXPECT_EQ("030/1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("030/12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("030/123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("030/1234", formatter_->InputDigit('4', &result_));
// 08021 2345
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("08", formatter_->InputDigit('8', &result_));
EXPECT_EQ("080", formatter_->InputDigit('0', &result_));
EXPECT_EQ("080 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("080 21", formatter_->InputDigit('1', &result_));
EXPECT_EQ("08021 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("08021 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("08021 234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("08021 2345", formatter_->InputDigit('5', &result_));
// 00 1 650 253 2250
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("00", formatter_->InputDigit('0', &result_));
EXPECT_EQ("00 1 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("00 1 6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("00 1 65", formatter_->InputDigit('5', &result_));
EXPECT_EQ("00 1 650", formatter_->InputDigit('0', &result_));
EXPECT_EQ("00 1 650 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("00 1 650 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("00 1 650 253", formatter_->InputDigit('3', &result_));
EXPECT_EQ("00 1 650 253 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("00 1 650 253 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("00 1 650 253 222", formatter_->InputDigit('2', &result_));
EXPECT_EQ("00 1 650 253 2222", formatter_->InputDigit('2', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_AR) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("AR"));
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 7", formatter_->InputDigit('7', &result_));
EXPECT_EQ("011 70", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 703", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 7031", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011 7031-3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("011 7031-30", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 7031-300", formatter_->InputDigit('0', &result_));
EXPECT_EQ("011 7031-3000", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_ARMobile) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("AR"));
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+54 ", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+54 9", formatter_->InputDigit('9', &result_));
EXPECT_EQ("+54 91", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+54 9 11", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+54 9 11 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+54 9 11 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+54 9 11 231", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+54 9 11 2312", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+54 9 11 2312 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+54 9 11 2312 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+54 9 11 2312 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+54 9 11 2312 1234", formatter_->InputDigit('4', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_KR) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("KR"));
// +82 51 234 5678
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 51", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+82 51-2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 51-23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+82 51-234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+82 51-234-5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 51-234-56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+82 51-234-567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+82 51-234-5678", formatter_->InputDigit('8', &result_));
// +82 2 531 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 25", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 2-53", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+82 2-531", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+82 2-531-5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 2-531-56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+82 2-531-567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+82 2-531-5678", formatter_->InputDigit('8', &result_));
// +82 2 3665 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+82 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+82 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+82 2-36", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+82 2-366", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+82 2-3665", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 2-3665-5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+82 2-3665-56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+82 2-3665-567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+82 2-3665-5678", formatter_->InputDigit('8', &result_));
// 02-114
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("02", formatter_->InputDigit('2', &result_));
EXPECT_EQ("021", formatter_->InputDigit('1', &result_));
EXPECT_EQ("02-11", formatter_->InputDigit('1', &result_));
EXPECT_EQ("02-114", formatter_->InputDigit('4', &result_));
// 02-1300
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("02", formatter_->InputDigit('2', &result_));
EXPECT_EQ("021", formatter_->InputDigit('1', &result_));
EXPECT_EQ("02-13", formatter_->InputDigit('3', &result_));
EXPECT_EQ("02-130", formatter_->InputDigit('0', &result_));
EXPECT_EQ("02-1300", formatter_->InputDigit('0', &result_));
// 011-456-7890
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011-4", formatter_->InputDigit('4', &result_));
EXPECT_EQ("011-45", formatter_->InputDigit('5', &result_));
EXPECT_EQ("011-456", formatter_->InputDigit('6', &result_));
EXPECT_EQ("011-456-7", formatter_->InputDigit('7', &result_));
EXPECT_EQ("011-456-78", formatter_->InputDigit('8', &result_));
EXPECT_EQ("011-456-789", formatter_->InputDigit('9', &result_));
EXPECT_EQ("011-456-7890", formatter_->InputDigit('0', &result_));
// 011-9876-7890
formatter_->Clear();
EXPECT_EQ("0", formatter_->InputDigit('0', &result_));
EXPECT_EQ("01", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011", formatter_->InputDigit('1', &result_));
EXPECT_EQ("011-9", formatter_->InputDigit('9', &result_));
EXPECT_EQ("011-98", formatter_->InputDigit('8', &result_));
EXPECT_EQ("011-987", formatter_->InputDigit('7', &result_));
EXPECT_EQ("011-9876", formatter_->InputDigit('6', &result_));
EXPECT_EQ("011-9876-7", formatter_->InputDigit('7', &result_));
EXPECT_EQ("011-9876-78", formatter_->InputDigit('8', &result_));
EXPECT_EQ("011-9876-789", formatter_->InputDigit('9', &result_));
EXPECT_EQ("011-9876-7890", formatter_->InputDigit('0', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_MX) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("MX"));
// +52 800 123 4567
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+52 80", formatter_->InputDigit('0', &result_));
EXPECT_EQ("+52 800", formatter_->InputDigit('0', &result_));
EXPECT_EQ("+52 800 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 800 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 800 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+52 800 123 4", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 800 123 45", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 800 123 456", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+52 800 123 4567", formatter_->InputDigit('7', &result_));
// +52 55 1234 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 55", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 55 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 55 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 55 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+52 55 1234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 55 1234 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 55 1234 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+52 55 1234 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+52 55 1234 5678", formatter_->InputDigit('8', &result_));
// +52 212 345 6789
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 21", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 212", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 212 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+52 212 34", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 212 345", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 212 345 6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+52 212 345 67", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+52 212 345 678", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+52 212 345 6789", formatter_->InputDigit('9', &result_));
// +52 1 55 1234 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 15", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 1 55", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 1 55 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 1 55 12", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 1 55 123", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+52 1 55 1234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 1 55 1234 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 1 55 1234 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+52 1 55 1234 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+52 1 55 1234 5678", formatter_->InputDigit('8', &result_));
// +52 1 541 234 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 ", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 1", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 15", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 1 54", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 1 541", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+52 1 541 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+52 1 541 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+52 1 541 234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+52 1 541 234 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+52 1 541 234 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+52 1 541 234 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+52 1 541 234 5678", formatter_->InputDigit('8', &result_));
}
TEST_F(AsYouTypeFormatterTest, AYTF_MultipleLeadingDigitPatterns) {
formatter_.reset(phone_util_.GetAsYouTypeFormatter("JP"));
// +81 50 2345 6789
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+81 50", formatter_->InputDigit('0', &result_));
EXPECT_EQ("+81 50 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 50 23", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+81 50 234", formatter_->InputDigit('4', &result_));
EXPECT_EQ("+81 50 2345", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+81 50 2345 6", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+81 50 2345 67", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+81 50 2345 678", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 50 2345 6789", formatter_->InputDigit('9', &result_));
// +81 222 12 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 22", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 22 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 22 21", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 2221 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 222 12 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+81 222 12 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+81 222 12 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+81 222 12 5678", formatter_->InputDigit('8', &result_));
// +81 3332 2 5678
formatter_->Clear();
EXPECT_EQ("+", formatter_->InputDigit('+', &result_));
EXPECT_EQ("+8", formatter_->InputDigit('8', &result_));
EXPECT_EQ("+81 ", formatter_->InputDigit('1', &result_));
EXPECT_EQ("+81 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+81 33", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+81 33 3", formatter_->InputDigit('3', &result_));
EXPECT_EQ("+81 3332", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 3332 2", formatter_->InputDigit('2', &result_));
EXPECT_EQ("+81 3332 2 5", formatter_->InputDigit('5', &result_));
EXPECT_EQ("+81 3332 2 56", formatter_->InputDigit('6', &result_));
EXPECT_EQ("+81 3332 2 567", formatter_->InputDigit('7', &result_));
EXPECT_EQ("+81 3332 2 5678", formatter_->InputDigit('8', &result_));
}
} // namespace phonenumbers
} // namespace i18n

+ 236
- 0
cpp/test/phonenumbers/unicodestring_test.cc View File

@ -0,0 +1,236 @@
// 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: Philippe Liard
#include <iostream>
#include <gtest/gtest.h>
#include "phonenumbers/unicodestring.h"
using std::ostream;
// Used by GTest to print the expected and actual results in case of failure.
ostream& operator<<(ostream& out, const i18n::phonenumbers::UnicodeString& s) {
string utf8;
s.toUTF8String(utf8);
out << utf8;
return out;
}
namespace i18n {
namespace phonenumbers {
TEST(UnicodeString, ToUTF8StringWithEmptyString) {
UnicodeString s;
string utf8;
s.toUTF8String(utf8);
EXPECT_EQ("", utf8);
}
TEST(UnicodeString, ToUTF8String) {
UnicodeString s("hello");
string utf8;
s.toUTF8String(utf8);
EXPECT_EQ("hello", utf8);
}
TEST(UnicodeString, ToUTF8StringWithNonAscii) {
UnicodeString s("\xEF\xBC\x95\xEF\xBC\x93" /* "53" */);
string utf8;
s.toUTF8String(utf8);
EXPECT_EQ("\xEF\xBC\x95\xEF\xBC\x93", utf8);
}
TEST(UnicodeString, AppendCodepoint) {
UnicodeString s;
s.append('h');
ASSERT_EQ(UnicodeString("h"), s);
s.append('e');
EXPECT_EQ(UnicodeString("he"), s);
}
TEST(UnicodeString, AppendCodepointWithNonAscii) {
UnicodeString s;
s.append(0xFF15 /* 5 */);
ASSERT_EQ(UnicodeString("\xEF\xBC\x95" /* 5 */), s);
s.append(0xFF13 /* 3 */);
EXPECT_EQ(UnicodeString("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */), s);
}
TEST(UnicodeString, AppendUnicodeString) {
UnicodeString s;
s.append(UnicodeString("he"));
ASSERT_EQ(UnicodeString("he"), s);
s.append(UnicodeString("llo"));
EXPECT_EQ(UnicodeString("hello"), s);
}
TEST(UnicodeString, AppendUnicodeStringWithNonAscii) {
UnicodeString s;
s.append(UnicodeString("\xEF\xBC\x95" /* 5 */));
ASSERT_EQ(UnicodeString("\xEF\xBC\x95"), s);
s.append(UnicodeString("\xEF\xBC\x93" /* 3 */));
EXPECT_EQ(UnicodeString("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */), s);
}
TEST(UnicodeString, IndexOf) {
UnicodeString s("hello");
EXPECT_EQ(0, s.indexOf('h'));
EXPECT_EQ(2, s.indexOf('l'));
EXPECT_EQ(4, s.indexOf('o'));
}
TEST(UnicodeString, IndexOfWithNonAscii) {
UnicodeString s("\xEF\xBC\x95\xEF\xBC\x93" /* 53 */);
EXPECT_EQ(1, s.indexOf(0xFF13 /* 3 */));
}
TEST(UnicodeString, ReplaceWithEmptyInputs) {
UnicodeString s;
s.replace(0, 0, UnicodeString(""));
EXPECT_EQ(UnicodeString(""), s);
}
TEST(UnicodeString, ReplaceWithEmptyReplacement) {
UnicodeString s("hello");
s.replace(0, 5, UnicodeString(""));
EXPECT_EQ(UnicodeString(""), s);
}
TEST(UnicodeString, ReplaceBegining) {
UnicodeString s("hello world");
s.replace(0, 5, UnicodeString("HELLO"));
EXPECT_EQ(UnicodeString("HELLO world"), s);
}
TEST(UnicodeString, ReplaceMiddle) {
UnicodeString s("hello world");
s.replace(5, 1, UnicodeString("AB"));
EXPECT_EQ(UnicodeString("helloABworld"), s);
}
TEST(UnicodeString, ReplaceEnd) {
UnicodeString s("hello world");
s.replace(10, 1, UnicodeString("AB"));
EXPECT_EQ(UnicodeString("hello worlAB"), s);
}
TEST(UnicodeString, ReplaceWithNonAscii) {
UnicodeString s("hello world");
s.replace(3, 2, UnicodeString("\xEF\xBC\x91\xEF\xBC\x90" /* 10 */));
EXPECT_EQ(UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90 world"), s);
}
TEST(UnicodeString, SetCharBegining) {
UnicodeString s("hello");
s.setCharAt(0, 'H');
EXPECT_EQ(UnicodeString("Hello"), s);
}
TEST(UnicodeString, SetCharMiddle) {
UnicodeString s("hello");
s.setCharAt(2, 'L');
EXPECT_EQ(UnicodeString("heLlo"), s);
}
TEST(UnicodeString, SetCharEnd) {
UnicodeString s("hello");
s.setCharAt(4, 'O');
EXPECT_EQ(UnicodeString("hellO"), s);
}
TEST(UnicodeString, SetCharWithNonAscii) {
UnicodeString s("hello");
s.setCharAt(4, 0xFF10 /* 0 */);
EXPECT_EQ(UnicodeString("hell\xEF\xBC\x90" /* 0 */), s);
}
TEST(UnicodeString, TempSubStringWithEmptyString) {
EXPECT_EQ(UnicodeString(""), UnicodeString().tempSubString(0, 0));
}
TEST(UnicodeString, TempSubStringWithInvalidInputs) {
UnicodeString s("hello");
// tempSubString() returns an empty unicode string if one of the provided
// paramaters is out of range.
EXPECT_EQ(UnicodeString(""), s.tempSubString(6));
EXPECT_EQ(UnicodeString(""), s.tempSubString(2, 6));
}
TEST(UnicodeString, TempSubString) {
UnicodeString s("hello");
EXPECT_EQ(UnicodeString(""), s.tempSubString(0, 0));
EXPECT_EQ(UnicodeString("h"), s.tempSubString(0, 1));
EXPECT_EQ(UnicodeString("hello"), s.tempSubString(0, 5));
EXPECT_EQ(UnicodeString("llo"), s.tempSubString(2, 3));
}
TEST(UnicodeString, TempSubStringWithNoLength) {
UnicodeString s("hello");
EXPECT_EQ(UnicodeString("hello"), s.tempSubString(0));
EXPECT_EQ(UnicodeString("llo"), s.tempSubString(2));
}
TEST(UnicodeString, TempSubStringWithNonAscii) {
UnicodeString s("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */);
EXPECT_EQ(UnicodeString("\xEF\xBC\x91" /* 1 */), s.tempSubString(3, 1));
}
TEST(UnicodeString, OperatorEqual) {
UnicodeString s("hello");
s = UnicodeString("Hello");
EXPECT_EQ(UnicodeString("Hello"), s);
}
TEST(UnicodeString, OperatorEqualWithNonAscii) {
UnicodeString s("hello");
s = UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */);
EXPECT_EQ(UnicodeString("hel\xEF\xBC\x91\xEF\xBC\x90"), s);
}
TEST(UnicodeString, OperatorBracket) {
UnicodeString s("hello");
EXPECT_EQ('h', s[0]);
EXPECT_EQ('e', s[1]);
EXPECT_EQ('l', s[2]);
EXPECT_EQ('l', s[3]);
EXPECT_EQ('o', s[4]);
}
TEST(UnicodeString, OperatorBracketWithNonAscii) {
UnicodeString s("hel\xEF\xBC\x91\xEF\xBC\x90" /* 10 */);
EXPECT_EQ('h', s[0]);
EXPECT_EQ('e', s[1]);
EXPECT_EQ('l', s[2]);
EXPECT_EQ(0xFF11 /* 1 */, s[3]);
EXPECT_EQ(0xFF10 /* 0 */, s[4]);
}
TEST(UnicodeString, OperatorBracketWithIteratorCacheInvalidation) {
UnicodeString s("hello");
EXPECT_EQ('h', s[0]);
EXPECT_EQ('e', s[1]);
// Modify the string which should invalidate the iterator cache.
s.setCharAt(1, 'E');
EXPECT_EQ(UnicodeString("hEllo"), s);
EXPECT_EQ('E', s[1]);
// Get the previous character which should invalidate the iterator cache.
EXPECT_EQ('h', s[0]);
EXPECT_EQ('o', s[4]);
}
} // namespace phonenumbers
} // namespace i18n

+ 8
- 2
tools/script/continuous-integration.sh View File

@ -33,12 +33,18 @@ test_cpp_version() {
# Write the program that tests the installation of the library to a temporary
# source file.
> $CC_TEST_FILE echo '
#include <cassert>
#include <base/memory/scoped_ptr.h>
#include <phonenumbers/asyoutypeformatter.h>
#include <phonenumbers/phonenumberutil.h>
using i18n::phonenumbers::AsYouTypeFormatter;
using i18n::phonenumbers::PhoneNumberUtil;
int main() {
PhoneNumberUtil* const phone_util = PhoneNumberUtil::GetInstance();
return phone_util == NULL;
const scoped_ptr<AsYouTypeFormatter> asytf(
phone_util->GetAsYouTypeFormatter("US"));
return !(phone_util != NULL && asytf != NULL);
}'
# Run the build and tests.
(


Loading…
Cancel
Save