/* * 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. */ package com.google.i18n.phonenumbers; import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat; import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata; import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection; import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc; import java.io.IOException; import java.io.StringReader; import java.util.Arrays; import java.util.regex.PatternSyntaxException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Unit tests for BuildMetadataFromXml.java * * @author Philippe Liard */ public class BuildMetadataFromXmlTest extends TestCase { // Helper method that outputs a DOM element from a XML string. private static Element parseXmlString(String xmlString) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputSource inputSource = new InputSource(); inputSource.setCharacterStream(new StringReader(xmlString)); return documentBuilder.parse(inputSource).getDocumentElement(); } // Tests validateRE(). public void testValidateRERemovesWhiteSpaces() { String input = " hello world "; // Should remove all the white spaces contained in the provided string. assertEquals("helloworld", BuildMetadataFromXml.validateRE(input, true)); // Make sure it only happens when the last parameter is set to true. assertEquals(" hello world ", BuildMetadataFromXml.validateRE(input, false)); } public void testValidateREThrowsException() { String invalidPattern = "["; // Should throw an exception when an invalid pattern is provided independently of the last // parameter (remove white spaces). try { BuildMetadataFromXml.validateRE(invalidPattern, false); fail(); } catch (PatternSyntaxException e) { // Test passed. } try { BuildMetadataFromXml.validateRE(invalidPattern, true); fail(); } catch (PatternSyntaxException e) { // Test passed. } // We don't allow | to be followed by ) because it introduces bugs, since we typically use it at // the end of each line and when a line is deleted, if the pipe from the previous line is not // removed, we end up erroneously accepting an empty group as well. String patternWithPipeFollowedByClosingParentheses = "|)"; try { BuildMetadataFromXml.validateRE(patternWithPipeFollowedByClosingParentheses, true); fail(); } catch (PatternSyntaxException e) { // Test passed. } String patternWithPipeFollowedByNewLineAndClosingParentheses = "|\n)"; try { BuildMetadataFromXml.validateRE(patternWithPipeFollowedByNewLineAndClosingParentheses, true); fail(); } catch (PatternSyntaxException e) { // Test passed. } } public void testValidateRE() { String validPattern = "[a-zA-Z]d{1,9}"; // The provided pattern should be left unchanged. assertEquals(validPattern, BuildMetadataFromXml.validateRE(validPattern, false)); } // Tests getNationalPrefix(). public void testGetNationalPrefix() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element territoryElement = parseXmlString(xmlInput); assertEquals("00", BuildMetadataFromXml.getNationalPrefix(territoryElement)); } // Tests loadTerritoryTagMetadata(). public void testLoadTerritoryTagMetadata() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder phoneMetadata = BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "0"); assertEquals(33, phoneMetadata.getCountryCode()); assertEquals("2", phoneMetadata.getLeadingDigits()); assertEquals("00", phoneMetadata.getInternationalPrefix()); assertEquals("00~11", phoneMetadata.getPreferredInternationalPrefix()); assertEquals("0", phoneMetadata.getNationalPrefixForParsing()); assertEquals("9$1", phoneMetadata.getNationalPrefixTransformRule()); assertEquals("0", phoneMetadata.getNationalPrefix()); assertEquals(" x", phoneMetadata.getPreferredExtnPrefix()); assertTrue(phoneMetadata.getMainCountryForCode()); assertTrue(phoneMetadata.isMobileNumberPortableRegion()); } public void testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder phoneMetadata = BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, ""); assertFalse(phoneMetadata.getMainCountryForCode()); assertFalse(phoneMetadata.isMobileNumberPortableRegion()); } public void testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder phoneMetadata = BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "00"); // When unspecified, nationalPrefixForParsing defaults to nationalPrefix. assertEquals("00", phoneMetadata.getNationalPrefix()); assertEquals(phoneMetadata.getNationalPrefix(), phoneMetadata.getNationalPrefixForParsing()); } public void testLoadTerritoryTagMetadataWithRequiredAttributesOnly() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element territoryElement = parseXmlString(xmlInput); // Should not throw any exception. BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, ""); } // Tests loadInternationalFormat(). public void testLoadInternationalFormat() throws ParserConfigurationException, SAXException, IOException { String intlFormat = "$1 $2"; String xmlInput = "" + intlFormat + ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat nationalFormat = NumberFormat.newBuilder().build(); assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat)); assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat()); } public void testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined() throws ParserConfigurationException, SAXException, IOException { String intlFormat = "$1 $2"; String xmlInput = "" + intlFormat + ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder nationalFormat = NumberFormat.newBuilder(); nationalFormat.setFormat("$1"); assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat.build())); assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat()); } public void testLoadInternationalFormatExpectsOnlyOnePattern() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); // Should throw an exception as multiple intlFormats are provided. try { BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, NumberFormat.newBuilder().build()); fail(); } catch (RuntimeException e) { // Test passed. } } public void testLoadInternationalFormatUsesNationalFormatByDefault() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder nationalFormat = NumberFormat.newBuilder(); String nationalPattern = "$1 $2 $3"; nationalFormat.setFormat(nationalPattern); assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat.build())); assertEquals(nationalPattern, metadata.getIntlNumberFormat(0).getFormat()); } public void testLoadInternationalFormatCopiesNationalFormatData() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder nationalFormat = NumberFormat.newBuilder(); nationalFormat.setFormat("$1-$2"); nationalFormat.setNationalPrefixOptionalWhenFormatting(true); assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat.build())); assertTrue(metadata.getIntlNumberFormat(0).getNationalPrefixOptionalWhenFormatting()); } public void testLoadNationalFormat() throws ParserConfigurationException, SAXException, IOException { String nationalFormat = "$1 $2"; String xmlInput = String.format("%s", nationalFormat); Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder numberFormat = NumberFormat.newBuilder(); BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat); assertEquals(nationalFormat, numberFormat.getFormat()); } public void testLoadNationalFormatRequiresFormat() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder numberFormat = NumberFormat.newBuilder(); try { BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat); fail(); } catch (RuntimeException e) { // Test passed. } } public void testLoadNationalFormatExpectsExactlyOneFormat() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder numberFormat = NumberFormat.newBuilder(); try { BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat); fail(); } catch (RuntimeException e) { // Test passed. } } // Tests loadAvailableFormats(). public void testLoadAvailableFormats() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " " + " $1 $2 $3" + " " + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "0", "", false /* NP not optional */); assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule()); assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule()); assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat()); } public void testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " " + " $1 $2 $3" + " " + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "0", "", false /* NP not optional */); assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule()); assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule()); assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat()); } public void testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " $1 $2 $3" + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "", "($1)", false /* NP not optional */); assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule()); } public void testLoadAvailableFormatsClearsIntlFormat() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " $1 $2 $3" + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "0", "($1)", false /* NP not optional */); assertEquals(0, metadata.getIntlNumberFormatCount()); } public void testLoadAvailableFormatsHandlesMultipleNumberFormats() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " $1 $2 $3" + " $1-$2" + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "0", "($1)", false /* NP not optional */); assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat()); assertEquals("$1-$2", metadata.getNumberFormat(1).getFormat()); } public void testLoadInternationalFormatDoesNotSetIntlFormatWhenNA() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "NA"; Element numberFormatElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); NumberFormat.Builder nationalFormat = NumberFormat.newBuilder(); nationalFormat.setFormat("$1 $2"); BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement, nationalFormat.build()); assertEquals(0, metadata.getIntlNumberFormatCount()); } // Tests setLeadingDigitsPatterns(). public void testSetLeadingDigitsPatterns() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + "12" + ""; Element numberFormatElement = parseXmlString(xmlInput); NumberFormat.Builder numberFormat = NumberFormat.newBuilder(); BuildMetadataFromXml.setLeadingDigitsPatterns(numberFormatElement, numberFormat); assertEquals("1", numberFormat.getLeadingDigitsPattern(0)); assertEquals("2", numberFormat.getLeadingDigitsPattern(1)); } // Tests setLeadingDigitsPatterns() in the case of international and national formatting rules // being present but not both defined for this numberFormat - we don't want to add them twice. public void testSetLeadingDigitsPatternsNotAddedTwiceWhenInternationalFormatsPresent() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " 1" + " $1" + " " + " " + " 2" + " $1" + " 9-$1" + " " + ""; Element element = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.loadAvailableFormats( metadata, element, "0", "", false /* NP not optional */); assertEquals(1, metadata.getNumberFormat(0).leadingDigitsPatternSize()); assertEquals(1, metadata.getNumberFormat(1).leadingDigitsPatternSize()); // When we merge the national format rules into the international format rules, we shouldn't add // the leading digit patterns multiple times. assertEquals(1, metadata.getIntlNumberFormat(0).leadingDigitsPatternSize()); assertEquals(1, metadata.getIntlNumberFormat(1).leadingDigitsPatternSize()); } // Tests getNationalPrefixFormattingRuleFromElement(). public void testGetNationalPrefixFormattingRuleFromElement() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element element = parseXmlString(xmlInput); assertEquals("0$1", BuildMetadataFromXml.getNationalPrefixFormattingRuleFromElement(element, "0")); } // Tests getDomesticCarrierCodeFormattingRuleFromElement(). public void testGetDomesticCarrierCodeFormattingRuleFromElement() throws ParserConfigurationException, SAXException, IOException { String xmlInput = ""; Element element = parseXmlString(xmlInput); assertEquals("0$CC $1", BuildMetadataFromXml.getDomesticCarrierCodeFormattingRuleFromElement(element, "0")); } // Tests processPhoneNumberDescElement(). public void testProcessPhoneNumberDescElementWithInvalidInput() throws ParserConfigurationException, SAXException, IOException { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); Element territoryElement = parseXmlString(""); PhoneNumberDesc.Builder phoneNumberDesc; phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "invalidType"); assertFalse(phoneNumberDesc.hasNationalNumberPattern()); } public void testProcessPhoneNumberDescElementOverridesGeneralDesc() throws ParserConfigurationException, SAXException, IOException { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.setNationalNumberPattern("\\d{8}"); String xmlInput = "" + " \\d{6}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneNumberDesc.Builder phoneNumberDesc; phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern()); } public void testBuildPhoneMetadataCollection_liteBuild() throws Exception { String xmlInput = "" + " " + " " + " " + " [1-9]\\d{7}" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " " + ""; Document document = parseXmlString(xmlInput).getOwnerDocument(); PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection( document, true, // liteBuild false, // specialBuild false, // isShortNumberMetadata false); // isAlternateFormatsMetadata assertTrue(metadataCollection.getMetadataCount() == 1); PhoneMetadata metadata = metadataCollection.getMetadataList().get(0); assertTrue(metadata.hasGeneralDesc()); assertFalse(metadata.getGeneralDesc().hasExampleNumber()); // Some Phonemetadata.java implementations may have custom logic, so we ensure this // implementation is doing the right thing by checking the value of the example number even when // hasExampleNumber is false. assertEquals("", metadata.getGeneralDesc().getExampleNumber()); assertTrue(metadata.hasFixedLine()); assertFalse(metadata.getFixedLine().hasExampleNumber()); assertEquals("", metadata.getFixedLine().getExampleNumber()); assertTrue(metadata.hasMobile()); assertFalse(metadata.getMobile().hasExampleNumber()); assertEquals("", metadata.getMobile().getExampleNumber()); } public void testBuildPhoneMetadataCollection_specialBuild() throws Exception { String xmlInput = "" + " " + " " + " " + " [1-9]\\d{7}" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " " + ""; Document document = parseXmlString(xmlInput).getOwnerDocument(); PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection( document, false, // liteBuild true, // specialBuild false, // isShortNumberMetadata false); // isAlternateFormatsMetadata assertTrue(metadataCollection.getMetadataCount() == 1); PhoneMetadata metadata = metadataCollection.getMetadataList().get(0); assertTrue(metadata.hasGeneralDesc()); assertFalse(metadata.getGeneralDesc().hasExampleNumber()); // Some Phonemetadata.java implementations may have custom logic, so we ensure this // implementation is doing the right thing by checking the value of the example number even when // hasExampleNumber is false. assertEquals("", metadata.getGeneralDesc().getExampleNumber()); // TODO: Consider clearing fixed-line if empty after being filtered. assertTrue(metadata.hasFixedLine()); assertFalse(metadata.getFixedLine().hasExampleNumber()); assertEquals("", metadata.getFixedLine().getExampleNumber()); assertTrue(metadata.hasMobile()); assertTrue(metadata.getMobile().hasExampleNumber()); assertEquals("10123456", metadata.getMobile().getExampleNumber()); } public void testBuildPhoneMetadataCollection_fullBuild() throws Exception { String xmlInput = "" + " " + " " + " " + " [1-9]\\d{7}" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " [1-9]\\d{7}" + " " + " 10123456" + " " + " " + " " + ""; Document document = parseXmlString(xmlInput).getOwnerDocument(); PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection( document, false, // liteBuild false, // specialBuild false, // isShortNumberMetadata false); // isAlternateFormatsMetadata assertTrue(metadataCollection.getMetadataCount() == 1); PhoneMetadata metadata = metadataCollection.getMetadataList().get(0); assertTrue(metadata.hasGeneralDesc()); assertFalse(metadata.getGeneralDesc().hasExampleNumber()); // Some Phonemetadata.java implementations may have custom logic, so we ensure this // implementation is doing the right thing by checking the value of the example number even when // hasExampleNumber is false. assertEquals("", metadata.getGeneralDesc().getExampleNumber()); assertTrue(metadata.hasFixedLine()); assertTrue(metadata.getFixedLine().hasExampleNumber()); assertEquals("10123456", metadata.getFixedLine().getExampleNumber()); assertTrue(metadata.hasMobile()); assertTrue(metadata.getMobile().hasExampleNumber()); assertEquals("10123456", metadata.getMobile().getExampleNumber()); } public void testProcessPhoneNumberDescOutputsExampleNumberByDefault() throws ParserConfigurationException, SAXException, IOException { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); String xmlInput = "" + " 01 01 01 01" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneNumberDesc.Builder phoneNumberDesc; phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); assertEquals("01 01 01 01", phoneNumberDesc.getExampleNumber()); } public void testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns() throws ParserConfigurationException, SAXException, IOException { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); String xmlInput = "" + " \t \\d { 6 } " + ""; Element countryElement = parseXmlString(xmlInput); PhoneNumberDesc.Builder phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, countryElement, "fixedLine"); assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern()); } // Tests setRelevantDescPatterns(). public void testSetRelevantDescPatternsSetsSameMobileAndFixedLinePattern() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " \\d{6}" + " \\d{6}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); // Should set sameMobileAndFixedPattern to true. BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement, false /* isShortNumberMetadata */); assertTrue(metadata.getSameMobileAndFixedLinePattern()); } public void testSetRelevantDescPatternsSetsAllDescriptionsForRegularLengthNumbers() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " \\d{1}" + " \\d{2}" + " \\d{3}" + " \\d{4}" + " \\d{5}" + " \\d{6}" + " \\d{7}" + " \\d{8}" + " \\d{9}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement, false /* isShortNumberMetadata */); assertEquals("\\d{1}", metadata.getFixedLine().getNationalNumberPattern()); assertEquals("\\d{2}", metadata.getMobile().getNationalNumberPattern()); assertEquals("\\d{3}", metadata.getPager().getNationalNumberPattern()); assertEquals("\\d{4}", metadata.getTollFree().getNationalNumberPattern()); assertEquals("\\d{5}", metadata.getPremiumRate().getNationalNumberPattern()); assertEquals("\\d{6}", metadata.getSharedCost().getNationalNumberPattern()); assertEquals("\\d{7}", metadata.getPersonalNumber().getNationalNumberPattern()); assertEquals("\\d{8}", metadata.getVoip().getNationalNumberPattern()); assertEquals("\\d{9}", metadata.getUan().getNationalNumberPattern()); } public void testSetRelevantDescPatternsSetsAllDescriptionsForShortNumbers() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " \\d{1}" + " \\d{2}" + " \\d{3}" + " \\d{4}" + " " + " \\d{5}" + " " + " " + " \\d{6}" + " " + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement, true /* isShortNumberMetadata */); assertEquals("\\d{1}", metadata.getTollFree().getNationalNumberPattern()); assertEquals("\\d{2}", metadata.getStandardRate().getNationalNumberPattern()); assertEquals("\\d{3}", metadata.getPremiumRate().getNationalNumberPattern()); assertEquals("\\d{4}", metadata.getShortCode().getNationalNumberPattern()); assertEquals("\\d{5}", metadata.getCarrierSpecific().getNationalNumberPattern()); assertEquals("\\d{6}", metadata.getSmsServices().getNationalNumberPattern()); } public void testSetRelevantDescPatternsThrowsErrorIfTypePresentMultipleTimes() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " \\d{6}" + " \\d{6}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder(); try { BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement, false /* isShortNumberMetadata */); fail("Fixed-line info present twice for France: we should fail."); } catch (RuntimeException expected) { assertEquals("Multiple elements with type fixedLine found.", expected.getMessage()); } } public void testAlternateFormatsOmitsDescPatterns() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " " + " 1" + " $1" + " " + " " + " \\d{1}" + " \\d{2}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement, false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build(); assertEquals("(1)(\\d{3})", metadata.getNumberFormat(0).getPattern()); assertEquals("1", metadata.getNumberFormat(0).getLeadingDigitsPattern(0)); assertEquals("$1", metadata.getNumberFormat(0).getFormat()); assertFalse(metadata.hasFixedLine()); assertNull(metadata.getFixedLine()); assertFalse(metadata.hasShortCode()); assertNull(metadata.getShortCode()); } public void testNationalPrefixRulesSetCorrectly() throws ParserConfigurationException, SAXException, IOException { String xmlInput = "" + " " + " " + " 1" + " $1" + " " + " " + " 2" + " $1" + " " + " " + " \\d{1}" + ""; Element territoryElement = parseXmlString(xmlInput); PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement, false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build(); assertTrue(metadata.getNumberFormat(0).getNationalPrefixOptionalWhenFormatting()); // This is inherited from the territory, with $NP replaced by the actual national prefix, and // $FG replaced with $1. assertEquals("0$1", metadata.getNumberFormat(0).getNationalPrefixFormattingRule()); // Here it is explicitly set to false. assertFalse(metadata.getNumberFormat(1).getNationalPrefixOptionalWhenFormatting()); } public void testProcessPhoneNumberDescElement_PossibleLengthsSetCorrectly() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); // The number lengths set for the general description must be a super-set of those in the // element being parsed. generalDesc.addPossibleLength(4); generalDesc.addPossibleLength(6); generalDesc.addPossibleLength(7); generalDesc.addPossibleLength(13); Element territoryElement = parseXmlString("" + "" // Sorting will be done when parsing. + " " + "" + ""); PhoneNumberDesc.Builder fixedLine; PhoneNumberDesc.Builder mobile; fixedLine = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); mobile = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "mobile"); assertEquals(2, fixedLine.getPossibleLengthCount()); assertEquals(4, fixedLine.getPossibleLength(0)); assertEquals(13, fixedLine.getPossibleLength(1)); assertEquals(1, fixedLine.getPossibleLengthLocalOnlyCount()); // We use [-1] to denote that there are no possible lengths; we don't leave it empty, since for // compression reasons, we use the empty list to mean that the generalDesc possible lengths // apply. assertEquals(1, mobile.getPossibleLengthCount()); assertEquals(-1, mobile.getPossibleLength(0)); assertEquals(0, mobile.getPossibleLengthLocalOnlyCount()); } public void testSetPossibleLengthsGeneralDesc_BuiltFromChildElements() throws Exception { Element territoryElement = parseXmlString("" + "" + " " + "" + "" + " " + "" + "" + " " + "" + ""); PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); BuildMetadataFromXml.setPossibleLengthsGeneralDesc( generalDesc, "someId", territoryElement, false /* not short-number metadata */); assertEquals(2, generalDesc.getPossibleLengthCount()); assertEquals(13, generalDesc.getPossibleLength(0)); // 15 is present twice in the input in different sections, but only once in the output. assertEquals(15, generalDesc.getPossibleLength(1)); assertEquals(2, generalDesc.getPossibleLengthLocalOnlyCount()); assertEquals(6, generalDesc.getPossibleLengthLocalOnly(0)); assertEquals(7, generalDesc.getPossibleLengthLocalOnly(1)); // 13 is skipped as a "local only" length, since it is also present as a normal length. } public void testSetPossibleLengthsGeneralDesc_IgnoresNoIntlDialling() throws Exception { Element territoryElement = parseXmlString("" + "" + " " + "" + "" + " " + "" + ""); PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); BuildMetadataFromXml.setPossibleLengthsGeneralDesc( generalDesc, "someId", territoryElement, false /* not short-number metadata */); assertEquals(1, generalDesc.getPossibleLengthCount()); assertEquals(13, generalDesc.getPossibleLength(0)); // 15 is skipped because noInternationalDialling should not contribute to the general lengths; // it isn't a particular "type" of number per se, it is a property that different types may // have. } public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadata() throws Exception { Element territoryElement = parseXmlString("" + "" + " " + "" + "" + " " + "" + "" + " " + "" + "" + " " + "" + ""); PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); BuildMetadataFromXml.setPossibleLengthsGeneralDesc( generalDesc, "someId", territoryElement, true /* short-number metadata */); // All elements other than shortCode are ignored when creating the general desc. assertEquals(2, generalDesc.getPossibleLengthCount()); assertEquals(6, generalDesc.getPossibleLength(0)); assertEquals(13, generalDesc.getPossibleLength(1)); } public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadataErrorsOnLocalLengths() throws Exception { Element territoryElement = parseXmlString("" + "" + " " + "" + ""); PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); try { BuildMetadataFromXml.setPossibleLengthsGeneralDesc( generalDesc, "someId", territoryElement, true /* short-number metadata */); fail(); } catch (RuntimeException expected) { // This should be an error, localOnly is not permitted in short-code metadata. assertEquals("Found local-only lengths in short-number metadata", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorDuplicates() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(6); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile"); fail("Invalid data seen: expected failure."); } catch (RuntimeException expected) { // This should be an error, 6 is seen twice. assertEquals("Duplicate length element found (6) in possibleLength string 6,6", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorDuplicatesOneLocal() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(6); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile"); fail("Invalid data seen: expected failure."); } catch (RuntimeException expected) { // This should be an error, 6 is seen twice. assertEquals("Possible length(s) found specified as a normal and local-only length: [6]", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorUncoveredLengths() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" // Sorting will be done when parsing. + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "noInternationalDialling"); fail("Lengths present not covered by the general desc: should fail."); } catch (RuntimeException expected) { // Lengths were present that the general description didn't know about. assertTrue(expected.getMessage().contains("Out-of-range possible length")); } } public void testProcessPhoneNumberDescElement_SameAsParent() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); // The number lengths set for the general description must be a super-set of those in the // element being parsed. generalDesc.addPossibleLength(4); generalDesc.addPossibleLength(6); generalDesc.addPossibleLength(7); generalDesc.addPossibleLengthLocalOnly(2); Element territoryElement = parseXmlString("" + "" // Sorting will be done when parsing. + " " + "" + ""); PhoneNumberDesc.Builder phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); // No possible lengths should be present, because they match the general description. assertEquals(0, phoneNumberDesc.getPossibleLengthCount()); // Local-only lengths should be present for child elements such as fixed-line. assertEquals(1, phoneNumberDesc.getPossibleLengthLocalOnlyCount()); } public void testProcessPhoneNumberDescElement_InvalidNumber() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("4d is not a number."); } catch (NumberFormatException expected) { assertEquals("For input string: \"4d\"", expected.getMessage()); } } public void testLoadCountryMetadata_GeneralDescHasNumberLengthsSet() throws Exception { Element territoryElement = parseXmlString("" + "" // This shouldn't be set, the possible lengths should be derived for generalDesc. + " " + "" + "" + " " + "" + ""); try { BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement, false /* isShortNumberMetadata */, false /* isAlternateFormatsMetadata */); fail("Possible lengths explicitly set for generalDesc and should not be: we should fail."); } catch (RuntimeException expected) { assertEquals("Found possible lengths specified at general desc: this should be derived" + " from child elements. Affected country: FR", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorEmptyPossibleLengthStringAttribute() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("Empty possible length string."); } catch (RuntimeException expected) { assertEquals("Empty possibleLength string found.", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorRangeSpecifiedWithComma() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("Ranges shouldn't use a comma."); } catch (RuntimeException expected) { assertEquals("Missing end of range character in possible length string [4,7].", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorIncompleteRange() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("Should fail: range incomplete."); } catch (RuntimeException expected) { assertEquals("Missing end of range character in possible length string [4-.", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorNoDashInRange() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("Should fail: range incomplete."); } catch (RuntimeException expected) { assertEquals("Ranges must have exactly one - character: missing for [4:10].", expected.getMessage()); } } public void testProcessPhoneNumberDescElement_ErrorRangeIsNotFromMinToMax() throws Exception { PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder(); generalDesc.addPossibleLength(4); Element territoryElement = parseXmlString("" + "" + " " + "" + ""); try { BuildMetadataFromXml.processPhoneNumberDescElement( generalDesc, territoryElement, "fixedLine"); fail("Should fail: range even."); } catch (RuntimeException expected) { assertEquals("The first number in a range should be two or more digits lower than the second." + " Culprit possibleLength string: [10-10]", expected.getMessage()); } } public void testGetMetadataFilter() { assertEquals(BuildMetadataFromXml.getMetadataFilter(false, false), MetadataFilter.emptyFilter()); assertEquals(BuildMetadataFromXml.getMetadataFilter(true, false), MetadataFilter.forLiteBuild()); assertEquals(BuildMetadataFromXml.getMetadataFilter(false, true), MetadataFilter.forSpecialBuild()); try { BuildMetadataFromXml.getMetadataFilter(true, true); fail("getMetadataFilter should fail when liteBuild and specialBuild are both set"); } catch (RuntimeException e) { // Test passed. } } }