1 /*
2  *  Copyright (C) 2011 The Libphonenumber Authors
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *  http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */
16 
17 package com.google.i18n.phonenumbers;
18 
19 import com.google.i18n.phonenumbers.Phonemetadata.NumberFormat;
20 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
21 import com.google.i18n.phonenumbers.Phonemetadata.PhoneMetadataCollection;
22 import com.google.i18n.phonenumbers.Phonemetadata.PhoneNumberDesc;
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.Arrays;
26 import java.util.regex.PatternSyntaxException;
27 import javax.xml.parsers.DocumentBuilder;
28 import javax.xml.parsers.DocumentBuilderFactory;
29 import javax.xml.parsers.ParserConfigurationException;
30 import junit.framework.TestCase;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.xml.sax.InputSource;
34 import org.xml.sax.SAXException;
35 
36 /**
37  * Unit tests for BuildMetadataFromXml.java
38  *
39  * @author Philippe Liard
40  */
41 public class BuildMetadataFromXmlTest extends TestCase {
42 
43   // Helper method that outputs a DOM element from a XML string.
parseXmlString(String xmlString)44   private static Element parseXmlString(String xmlString)
45       throws ParserConfigurationException, SAXException, IOException {
46     DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
47     DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
48     InputSource inputSource = new InputSource();
49     inputSource.setCharacterStream(new StringReader(xmlString));
50     return documentBuilder.parse(inputSource).getDocumentElement();
51   }
52 
53   // Tests validateRE().
testValidateRERemovesWhiteSpaces()54   public void testValidateRERemovesWhiteSpaces() {
55     String input = " hello world ";
56     // Should remove all the white spaces contained in the provided string.
57     assertEquals("helloworld", BuildMetadataFromXml.validateRE(input, true));
58     // Make sure it only happens when the last parameter is set to true.
59     assertEquals(" hello world ", BuildMetadataFromXml.validateRE(input, false));
60   }
61 
testValidateREThrowsException()62   public void testValidateREThrowsException() {
63     String invalidPattern = "[";
64     // Should throw an exception when an invalid pattern is provided independently of the last
65     // parameter (remove white spaces).
66     try {
67       BuildMetadataFromXml.validateRE(invalidPattern, false);
68       fail();
69     } catch (PatternSyntaxException e) {
70       // Test passed.
71     }
72     try {
73       BuildMetadataFromXml.validateRE(invalidPattern, true);
74       fail();
75     } catch (PatternSyntaxException e) {
76       // Test passed.
77     }
78     // We don't allow | to be followed by ) because it introduces bugs, since we typically use it at
79     // the end of each line and when a line is deleted, if the pipe from the previous line is not
80     // removed, we end up erroneously accepting an empty group as well.
81     String patternWithPipeFollowedByClosingParentheses = "|)";
82     try {
83       BuildMetadataFromXml.validateRE(patternWithPipeFollowedByClosingParentheses, true);
84       fail();
85     } catch (PatternSyntaxException e) {
86       // Test passed.
87     }
88     String patternWithPipeFollowedByNewLineAndClosingParentheses = "|\n)";
89     try {
90       BuildMetadataFromXml.validateRE(patternWithPipeFollowedByNewLineAndClosingParentheses, true);
91       fail();
92     } catch (PatternSyntaxException e) {
93       // Test passed.
94     }
95   }
96 
testValidateRE()97   public void testValidateRE() {
98     String validPattern = "[a-zA-Z]d{1,9}";
99     // The provided pattern should be left unchanged.
100     assertEquals(validPattern, BuildMetadataFromXml.validateRE(validPattern, false));
101   }
102 
103   // Tests getNationalPrefix().
testGetNationalPrefix()104   public void testGetNationalPrefix()
105       throws ParserConfigurationException, SAXException, IOException {
106     String xmlInput = "<territory nationalPrefix='00'/>";
107     Element territoryElement = parseXmlString(xmlInput);
108     assertEquals("00", BuildMetadataFromXml.getNationalPrefix(territoryElement));
109   }
110 
111   // Tests loadTerritoryTagMetadata().
testLoadTerritoryTagMetadata()112   public void testLoadTerritoryTagMetadata()
113       throws ParserConfigurationException, SAXException, IOException {
114     String xmlInput = "<territory"
115         + "  countryCode='33' leadingDigits='2' internationalPrefix='00'"
116         + "  preferredInternationalPrefix='0011' nationalPrefixForParsing='0'"
117         + "  nationalPrefixTransformRule='9$1'"  // nationalPrefix manually injected.
118         + "  preferredExtnPrefix=' x' mainCountryForCode='true'"
119         + "  leadingZeroPossible='true' mobileNumberPortableRegion='true'>"
120         + "</territory>";
121     Element territoryElement = parseXmlString(xmlInput);
122     PhoneMetadata.Builder phoneMetadata =
123         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "0");
124     assertEquals(33, phoneMetadata.getCountryCode());
125     assertEquals("2", phoneMetadata.getLeadingDigits());
126     assertEquals("00", phoneMetadata.getInternationalPrefix());
127     assertEquals("0011", phoneMetadata.getPreferredInternationalPrefix());
128     assertEquals("0", phoneMetadata.getNationalPrefixForParsing());
129     assertEquals("9$1", phoneMetadata.getNationalPrefixTransformRule());
130     assertEquals("0", phoneMetadata.getNationalPrefix());
131     assertEquals(" x", phoneMetadata.getPreferredExtnPrefix());
132     assertTrue(phoneMetadata.getMainCountryForCode());
133     assertTrue(phoneMetadata.isMobileNumberPortableRegion());
134   }
135 
testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault()136   public void testLoadTerritoryTagMetadataSetsBooleanFieldsToFalseByDefault()
137       throws ParserConfigurationException, SAXException, IOException {
138     String xmlInput = "<territory countryCode='33'/>";
139     Element territoryElement = parseXmlString(xmlInput);
140     PhoneMetadata.Builder phoneMetadata =
141         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "");
142     assertFalse(phoneMetadata.getMainCountryForCode());
143     assertFalse(phoneMetadata.isMobileNumberPortableRegion());
144   }
145 
testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault()146   public void testLoadTerritoryTagMetadataSetsNationalPrefixForParsingByDefault()
147       throws ParserConfigurationException, SAXException, IOException {
148     String xmlInput = "<territory countryCode='33'/>";
149     Element territoryElement = parseXmlString(xmlInput);
150     PhoneMetadata.Builder phoneMetadata =
151         BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "00");
152     // When unspecified, nationalPrefixForParsing defaults to nationalPrefix.
153     assertEquals("00", phoneMetadata.getNationalPrefix());
154     assertEquals(phoneMetadata.getNationalPrefix(), phoneMetadata.getNationalPrefixForParsing());
155   }
156 
testLoadTerritoryTagMetadataWithRequiredAttributesOnly()157   public void testLoadTerritoryTagMetadataWithRequiredAttributesOnly()
158       throws ParserConfigurationException, SAXException, IOException {
159     String xmlInput = "<territory countryCode='33' internationalPrefix='00'/>";
160     Element territoryElement = parseXmlString(xmlInput);
161     // Should not throw any exception.
162     BuildMetadataFromXml.loadTerritoryTagMetadata("33", territoryElement, "");
163   }
164 
165   // Tests loadInternationalFormat().
testLoadInternationalFormat()166   public void testLoadInternationalFormat()
167       throws ParserConfigurationException, SAXException, IOException {
168     String intlFormat = "$1 $2";
169     String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
170     Element numberFormatElement = parseXmlString(xmlInput);
171     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
172     NumberFormat nationalFormat = NumberFormat.newBuilder().build();
173 
174     assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
175                                                             nationalFormat));
176     assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
177   }
178 
testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined()179   public void testLoadInternationalFormatWithBothNationalAndIntlFormatsDefined()
180       throws ParserConfigurationException, SAXException, IOException {
181     String intlFormat = "$1 $2";
182     String xmlInput = "<numberFormat><intlFormat>" + intlFormat + "</intlFormat></numberFormat>";
183     Element numberFormatElement = parseXmlString(xmlInput);
184     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
185     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
186     nationalFormat.setFormat("$1");
187 
188     assertTrue(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
189                                                             nationalFormat.build()));
190     assertEquals(intlFormat, metadata.getIntlNumberFormat(0).getFormat());
191   }
192 
testLoadInternationalFormatExpectsOnlyOnePattern()193   public void testLoadInternationalFormatExpectsOnlyOnePattern()
194       throws ParserConfigurationException, SAXException, IOException {
195     String xmlInput = "<numberFormat><intlFormat/><intlFormat/></numberFormat>";
196     Element numberFormatElement = parseXmlString(xmlInput);
197     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
198 
199     // Should throw an exception as multiple intlFormats are provided.
200     try {
201       BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
202                                                    NumberFormat.newBuilder().build());
203       fail();
204     } catch (RuntimeException e) {
205       // Test passed.
206     }
207   }
208 
testLoadInternationalFormatUsesNationalFormatByDefault()209   public void testLoadInternationalFormatUsesNationalFormatByDefault()
210       throws ParserConfigurationException, SAXException, IOException {
211     String xmlInput = "<numberFormat></numberFormat>";
212     Element numberFormatElement = parseXmlString(xmlInput);
213     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
214     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
215     String nationalPattern = "$1 $2 $3";
216     nationalFormat.setFormat(nationalPattern);
217 
218     assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
219                                                              nationalFormat.build()));
220     assertEquals(nationalPattern, metadata.getIntlNumberFormat(0).getFormat());
221   }
222 
testLoadInternationalFormatCopiesNationalFormatData()223   public void testLoadInternationalFormatCopiesNationalFormatData()
224       throws ParserConfigurationException, SAXException, IOException {
225     String xmlInput = "<numberFormat></numberFormat>";
226     Element numberFormatElement = parseXmlString(xmlInput);
227     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
228     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
229     nationalFormat.setFormat("$1-$2");
230     nationalFormat.setNationalPrefixOptionalWhenFormatting(true);
231 
232     assertFalse(BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
233                                                              nationalFormat.build()));
234     assertTrue(metadata.getIntlNumberFormat(0).getNationalPrefixOptionalWhenFormatting());
235   }
236 
testLoadNationalFormat()237   public void testLoadNationalFormat()
238       throws ParserConfigurationException, SAXException, IOException {
239     String nationalFormat = "$1 $2";
240     String xmlInput = String.format("<numberFormat><format>%s</format></numberFormat>",
241                                     nationalFormat);
242     Element numberFormatElement = parseXmlString(xmlInput);
243     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
244     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
245     BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
246     assertEquals(nationalFormat, numberFormat.getFormat());
247   }
248 
testLoadNationalFormatRequiresFormat()249   public void testLoadNationalFormatRequiresFormat()
250       throws ParserConfigurationException, SAXException, IOException {
251     String xmlInput = "<numberFormat></numberFormat>";
252     Element numberFormatElement = parseXmlString(xmlInput);
253     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
254     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
255 
256     try {
257       BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
258       fail();
259     } catch (RuntimeException e) {
260       // Test passed.
261     }
262   }
263 
testLoadNationalFormatExpectsExactlyOneFormat()264   public void testLoadNationalFormatExpectsExactlyOneFormat()
265       throws ParserConfigurationException, SAXException, IOException {
266     String xmlInput = "<numberFormat><format/><format/></numberFormat>";
267     Element numberFormatElement = parseXmlString(xmlInput);
268     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
269     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
270 
271     try {
272       BuildMetadataFromXml.loadNationalFormat(metadata, numberFormatElement, numberFormat);
273       fail();
274     } catch (RuntimeException e) {
275       // Test passed.
276     }
277   }
278 
279   // Tests loadAvailableFormats().
testLoadAvailableFormats()280   public void testLoadAvailableFormats()
281       throws ParserConfigurationException, SAXException, IOException {
282     String xmlInput = "<territory>"
283         + "  <availableFormats>"
284         + "    <numberFormat nationalPrefixFormattingRule='($FG)'"
285         + "                  carrierCodeFormattingRule='$NP $CC ($FG)'>"
286         + "      <format>$1 $2 $3</format>"
287         + "    </numberFormat>"
288         + "  </availableFormats>"
289         + "</territory>";
290     Element element = parseXmlString(xmlInput);
291     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
292     BuildMetadataFromXml.loadAvailableFormats(
293         metadata, element, "0", "", false /* NP not optional */);
294     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
295     assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
296     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
297   }
298 
testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule()299   public void testLoadAvailableFormatsPropagatesCarrierCodeFormattingRule()
300       throws ParserConfigurationException, SAXException, IOException {
301     String xmlInput =
302         "<territory carrierCodeFormattingRule='$NP $CC ($FG)'>"
303         + "  <availableFormats>"
304         + "    <numberFormat nationalPrefixFormattingRule='($FG)'>"
305         + "      <format>$1 $2 $3</format>"
306         + "    </numberFormat>"
307         + "  </availableFormats>"
308         + "</territory>";
309     Element element = parseXmlString(xmlInput);
310     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
311     BuildMetadataFromXml.loadAvailableFormats(
312         metadata, element, "0", "", false /* NP not optional */);
313     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
314     assertEquals("0 $CC ($1)", metadata.getNumberFormat(0).getDomesticCarrierCodeFormattingRule());
315     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
316   }
317 
testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule()318   public void testLoadAvailableFormatsSetsProvidedNationalPrefixFormattingRule()
319       throws ParserConfigurationException, SAXException, IOException {
320     String xmlInput = "<territory>"
321         + "  <availableFormats>"
322         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
323         + "  </availableFormats>"
324         + "</territory>";
325     Element element = parseXmlString(xmlInput);
326     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
327     BuildMetadataFromXml.loadAvailableFormats(
328         metadata, element, "", "($1)", false /* NP not optional */);
329     assertEquals("($1)", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
330   }
331 
testLoadAvailableFormatsClearsIntlFormat()332   public void testLoadAvailableFormatsClearsIntlFormat()
333       throws ParserConfigurationException, SAXException, IOException {
334     String xmlInput = "<territory>"
335         + "  <availableFormats>"
336         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
337         + "  </availableFormats>"
338         + "</territory>";
339     Element element = parseXmlString(xmlInput);
340     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
341     BuildMetadataFromXml.loadAvailableFormats(
342         metadata, element, "0", "($1)", false /* NP not optional */);
343     assertEquals(0, metadata.intlNumberFormatSize());
344   }
345 
testLoadAvailableFormatsHandlesMultipleNumberFormats()346   public void testLoadAvailableFormatsHandlesMultipleNumberFormats()
347       throws ParserConfigurationException, SAXException, IOException {
348     String xmlInput = "<territory>"
349         + "  <availableFormats>"
350         + "    <numberFormat><format>$1 $2 $3</format></numberFormat>"
351         + "    <numberFormat><format>$1-$2</format></numberFormat>"
352         + "  </availableFormats>"
353         + "</territory>";
354     Element element = parseXmlString(xmlInput);
355     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
356     BuildMetadataFromXml.loadAvailableFormats(
357         metadata, element, "0", "($1)", false /* NP not optional */);
358     assertEquals("$1 $2 $3", metadata.getNumberFormat(0).getFormat());
359     assertEquals("$1-$2", metadata.getNumberFormat(1).getFormat());
360   }
361 
testLoadInternationalFormatDoesNotSetIntlFormatWhenNA()362   public void testLoadInternationalFormatDoesNotSetIntlFormatWhenNA()
363       throws ParserConfigurationException, SAXException, IOException {
364     String xmlInput = "<numberFormat><intlFormat>NA</intlFormat></numberFormat>";
365     Element numberFormatElement = parseXmlString(xmlInput);
366     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
367     NumberFormat.Builder nationalFormat = NumberFormat.newBuilder();
368     nationalFormat.setFormat("$1 $2");
369 
370     BuildMetadataFromXml.loadInternationalFormat(metadata, numberFormatElement,
371                                                  nationalFormat.build());
372     assertEquals(0, metadata.intlNumberFormatSize());
373   }
374 
375   // Tests setLeadingDigitsPatterns().
testSetLeadingDigitsPatterns()376   public void testSetLeadingDigitsPatterns()
377       throws ParserConfigurationException, SAXException, IOException {
378     String xmlInput = "<numberFormat>"
379         + "<leadingDigits>1</leadingDigits><leadingDigits>2</leadingDigits>"
380         + "</numberFormat>";
381     Element numberFormatElement = parseXmlString(xmlInput);
382     NumberFormat.Builder numberFormat = NumberFormat.newBuilder();
383     BuildMetadataFromXml.setLeadingDigitsPatterns(numberFormatElement, numberFormat);
384 
385     assertEquals("1", numberFormat.getLeadingDigitsPattern(0));
386     assertEquals("2", numberFormat.getLeadingDigitsPattern(1));
387   }
388 
389   // Tests setLeadingDigitsPatterns() in the case of international and national formatting rules
390   // being present but not both defined for this numberFormat - we don't want to add them twice.
testSetLeadingDigitsPatternsNotAddedTwiceWhenInternationalFormatsPresent()391   public void testSetLeadingDigitsPatternsNotAddedTwiceWhenInternationalFormatsPresent()
392       throws ParserConfigurationException, SAXException, IOException {
393     String xmlInput = "<availableFormats>"
394         + "  <numberFormat pattern=\"(1)(\\d{3})\">"
395         + "    <leadingDigits>1</leadingDigits>"
396         + "    <format>$1</format>"
397         + "  </numberFormat>"
398         + "  <numberFormat pattern=\"(2)(\\d{3})\">"
399         + "    <leadingDigits>2</leadingDigits>"
400         + "    <format>$1</format>"
401         + "    <intlFormat>9-$1</intlFormat>"
402         + "  </numberFormat>"
403         + "</availableFormats>";
404     Element element = parseXmlString(xmlInput);
405     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
406     BuildMetadataFromXml.loadAvailableFormats(
407         metadata, element, "0", "", false /* NP not optional */);
408     assertEquals(1, metadata.getNumberFormat(0).leadingDigitsPatternSize());
409     assertEquals(1, metadata.getNumberFormat(1).leadingDigitsPatternSize());
410     // When we merge the national format rules into the international format rules, we shouldn't add
411     // the leading digit patterns multiple times.
412     assertEquals(1, metadata.getIntlNumberFormat(0).leadingDigitsPatternSize());
413     assertEquals(1, metadata.getIntlNumberFormat(1).leadingDigitsPatternSize());
414   }
415 
416   // Tests getNationalPrefixFormattingRuleFromElement().
testGetNationalPrefixFormattingRuleFromElement()417   public void testGetNationalPrefixFormattingRuleFromElement()
418       throws ParserConfigurationException, SAXException, IOException {
419     String xmlInput = "<territory nationalPrefixFormattingRule='$NP$FG'/>";
420     Element element = parseXmlString(xmlInput);
421     assertEquals("0$1",
422                  BuildMetadataFromXml.getNationalPrefixFormattingRuleFromElement(element, "0"));
423   }
424 
425   // Tests getDomesticCarrierCodeFormattingRuleFromElement().
testGetDomesticCarrierCodeFormattingRuleFromElement()426   public void testGetDomesticCarrierCodeFormattingRuleFromElement()
427       throws ParserConfigurationException, SAXException, IOException {
428     String xmlInput = "<territory carrierCodeFormattingRule='$NP$CC $FG'/>";
429     Element element = parseXmlString(xmlInput);
430     assertEquals("0$CC $1",
431                  BuildMetadataFromXml.getDomesticCarrierCodeFormattingRuleFromElement(element,
432                                                                                       "0"));
433   }
434 
435   // Tests processPhoneNumberDescElement().
testProcessPhoneNumberDescElementWithInvalidInput()436   public void testProcessPhoneNumberDescElementWithInvalidInput()
437       throws ParserConfigurationException, SAXException, IOException {
438     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
439     Element territoryElement = parseXmlString("<territory/>");
440     PhoneNumberDesc.Builder phoneNumberDesc;
441 
442     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
443         generalDesc, territoryElement, "invalidType");
444     assertFalse(phoneNumberDesc.hasNationalNumberPattern());
445   }
446 
testProcessPhoneNumberDescElementOverridesGeneralDesc()447   public void testProcessPhoneNumberDescElementOverridesGeneralDesc()
448       throws ParserConfigurationException, SAXException, IOException {
449     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
450     generalDesc.setNationalNumberPattern("\\d{8}");
451     String xmlInput = "<territory><fixedLine>"
452         + "  <nationalNumberPattern>\\d{6}</nationalNumberPattern>"
453         + "</fixedLine></territory>";
454     Element territoryElement = parseXmlString(xmlInput);
455     PhoneNumberDesc.Builder phoneNumberDesc;
456 
457     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
458         generalDesc, territoryElement, "fixedLine");
459     assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern());
460   }
461 
testBuildPhoneMetadataCollection_liteBuild()462   public void testBuildPhoneMetadataCollection_liteBuild() throws Exception {
463     String xmlInput =
464         "<phoneNumberMetadata>"
465         + "  <territories>"
466         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
467         + "      <generalDesc>"
468         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
469         + "      </generalDesc>"
470         + "      <fixedLine>"
471         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
472         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
473         + "        <exampleNumber>10123456</exampleNumber>"
474         + "      </fixedLine>"
475         + "      <mobile>"
476         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
477         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
478         + "        <exampleNumber>10123456</exampleNumber>"
479         + "      </mobile>"
480         + "    </territory>"
481         + "  </territories>"
482         + "</phoneNumberMetadata>";
483     Document document = parseXmlString(xmlInput).getOwnerDocument();
484 
485     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
486         document,
487         true,  // liteBuild
488         false,  // specialBuild
489         false,  // isShortNumberMetadata
490         false);  // isAlternateFormatsMetadata
491 
492     assertTrue(metadataCollection.getMetadataCount() == 1);
493     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
494     assertTrue(metadata.hasGeneralDesc());
495     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
496     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
497     // implementation is doing the right thing by checking the value of the example number even when
498     // hasExampleNumber is false.
499     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
500     assertTrue(metadata.hasFixedLine());
501     assertFalse(metadata.getFixedLine().hasExampleNumber());
502     assertEquals("", metadata.getFixedLine().getExampleNumber());
503     assertTrue(metadata.hasMobile());
504     assertFalse(metadata.getMobile().hasExampleNumber());
505     assertEquals("", metadata.getMobile().getExampleNumber());
506   }
507 
testBuildPhoneMetadataCollection_specialBuild()508   public void testBuildPhoneMetadataCollection_specialBuild() throws Exception {
509     String xmlInput =
510         "<phoneNumberMetadata>"
511         + "  <territories>"
512         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
513         + "      <generalDesc>"
514         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
515         + "      </generalDesc>"
516         + "      <fixedLine>"
517         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
518         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
519         + "        <exampleNumber>10123456</exampleNumber>"
520         + "      </fixedLine>"
521         + "      <mobile>"
522         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
523         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
524         + "        <exampleNumber>10123456</exampleNumber>"
525         + "      </mobile>"
526         + "    </territory>"
527         + "  </territories>"
528         + "</phoneNumberMetadata>";
529     Document document = parseXmlString(xmlInput).getOwnerDocument();
530 
531     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
532         document,
533         false,  // liteBuild
534         true,  // specialBuild
535         false,  // isShortNumberMetadata
536         false);  // isAlternateFormatsMetadata
537 
538     assertTrue(metadataCollection.getMetadataCount() == 1);
539     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
540     assertTrue(metadata.hasGeneralDesc());
541     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
542     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
543     // implementation is doing the right thing by checking the value of the example number even when
544     // hasExampleNumber is false.
545     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
546     // TODO: Consider clearing fixed-line if empty after being filtered.
547     assertTrue(metadata.hasFixedLine());
548     assertFalse(metadata.getFixedLine().hasExampleNumber());
549     assertEquals("", metadata.getFixedLine().getExampleNumber());
550     assertTrue(metadata.hasMobile());
551     assertTrue(metadata.getMobile().hasExampleNumber());
552     assertEquals("10123456", metadata.getMobile().getExampleNumber());
553   }
554 
testBuildPhoneMetadataCollection_fullBuild()555   public void testBuildPhoneMetadataCollection_fullBuild() throws Exception {
556     String xmlInput =
557         "<phoneNumberMetadata>"
558         + "  <territories>"
559         + "    <territory id=\"AM\" countryCode=\"374\" internationalPrefix=\"00\">"
560         + "      <generalDesc>"
561         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
562         + "      </generalDesc>"
563         + "      <fixedLine>"
564         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
565         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
566         + "        <exampleNumber>10123456</exampleNumber>"
567         + "      </fixedLine>"
568         + "      <mobile>"
569         + "        <nationalNumberPattern>[1-9]\\d{7}</nationalNumberPattern>"
570         + "        <possibleLengths national=\"8\" localOnly=\"5,6\"/>"
571         + "        <exampleNumber>10123456</exampleNumber>"
572         + "      </mobile>"
573         + "    </territory>"
574         + "  </territories>"
575         + "</phoneNumberMetadata>";
576     Document document = parseXmlString(xmlInput).getOwnerDocument();
577 
578     PhoneMetadataCollection metadataCollection = BuildMetadataFromXml.buildPhoneMetadataCollection(
579         document,
580         false,  // liteBuild
581         false,  // specialBuild
582         false,  // isShortNumberMetadata
583         false);  // isAlternateFormatsMetadata
584 
585     assertTrue(metadataCollection.getMetadataCount() == 1);
586     PhoneMetadata metadata = metadataCollection.getMetadataList().get(0);
587     assertTrue(metadata.hasGeneralDesc());
588     assertFalse(metadata.getGeneralDesc().hasExampleNumber());
589     // Some Phonemetadata.java implementations may have custom logic, so we ensure this
590     // implementation is doing the right thing by checking the value of the example number even when
591     // hasExampleNumber is false.
592     assertEquals("", metadata.getGeneralDesc().getExampleNumber());
593     assertTrue(metadata.hasFixedLine());
594     assertTrue(metadata.getFixedLine().hasExampleNumber());
595     assertEquals("10123456", metadata.getFixedLine().getExampleNumber());
596     assertTrue(metadata.hasMobile());
597     assertTrue(metadata.getMobile().hasExampleNumber());
598     assertEquals("10123456", metadata.getMobile().getExampleNumber());
599   }
600 
testProcessPhoneNumberDescOutputsExampleNumberByDefault()601   public void testProcessPhoneNumberDescOutputsExampleNumberByDefault()
602       throws ParserConfigurationException, SAXException, IOException {
603     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
604     String xmlInput = "<territory><fixedLine>"
605         + "  <exampleNumber>01 01 01 01</exampleNumber>"
606         + "</fixedLine></territory>";
607     Element territoryElement = parseXmlString(xmlInput);
608     PhoneNumberDesc.Builder phoneNumberDesc;
609 
610     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
611         generalDesc, territoryElement, "fixedLine");
612     assertEquals("01 01 01 01", phoneNumberDesc.getExampleNumber());
613   }
614 
testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns()615   public void testProcessPhoneNumberDescRemovesWhiteSpacesInPatterns()
616       throws ParserConfigurationException, SAXException, IOException {
617     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
618     String xmlInput = "<territory><fixedLine>"
619         + "  <nationalNumberPattern>\t \\d { 6 } </nationalNumberPattern>"
620         + "</fixedLine></territory>";
621     Element countryElement = parseXmlString(xmlInput);
622     PhoneNumberDesc.Builder phoneNumberDesc;
623 
624     phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
625         generalDesc, countryElement, "fixedLine");
626     assertEquals("\\d{6}", phoneNumberDesc.getNationalNumberPattern());
627   }
628 
629   // Tests setRelevantDescPatterns().
testSetRelevantDescPatternsSetsSameMobileAndFixedLinePattern()630   public void testSetRelevantDescPatternsSetsSameMobileAndFixedLinePattern()
631       throws ParserConfigurationException, SAXException, IOException {
632     String xmlInput = "<territory countryCode=\"33\">"
633         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
634         + "  <mobile><nationalNumberPattern>\\d{6}</nationalNumberPattern></mobile>"
635         + "</territory>";
636     Element territoryElement = parseXmlString(xmlInput);
637     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
638     // Should set sameMobileAndFixedPattern to true.
639     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
640         false /* isShortNumberMetadata */);
641     assertTrue(metadata.getSameMobileAndFixedLinePattern());
642   }
643 
testSetRelevantDescPatternsSetsAllDescriptionsForRegularLengthNumbers()644   public void testSetRelevantDescPatternsSetsAllDescriptionsForRegularLengthNumbers()
645       throws ParserConfigurationException, SAXException, IOException {
646     String xmlInput = "<territory countryCode=\"33\">"
647         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
648         + "  <mobile><nationalNumberPattern>\\d{2}</nationalNumberPattern></mobile>"
649         + "  <pager><nationalNumberPattern>\\d{3}</nationalNumberPattern></pager>"
650         + "  <tollFree><nationalNumberPattern>\\d{4}</nationalNumberPattern></tollFree>"
651         + "  <premiumRate><nationalNumberPattern>\\d{5}</nationalNumberPattern></premiumRate>"
652         + "  <sharedCost><nationalNumberPattern>\\d{6}</nationalNumberPattern></sharedCost>"
653         + "  <personalNumber><nationalNumberPattern>\\d{7}</nationalNumberPattern></personalNumber>"
654         + "  <voip><nationalNumberPattern>\\d{8}</nationalNumberPattern></voip>"
655         + "  <uan><nationalNumberPattern>\\d{9}</nationalNumberPattern></uan>"
656         + "</territory>";
657     Element territoryElement = parseXmlString(xmlInput);
658     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
659     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
660         false /* isShortNumberMetadata */);
661     assertEquals("\\d{1}", metadata.getFixedLine().getNationalNumberPattern());
662     assertEquals("\\d{2}", metadata.getMobile().getNationalNumberPattern());
663     assertEquals("\\d{3}", metadata.getPager().getNationalNumberPattern());
664     assertEquals("\\d{4}", metadata.getTollFree().getNationalNumberPattern());
665     assertEquals("\\d{5}", metadata.getPremiumRate().getNationalNumberPattern());
666     assertEquals("\\d{6}", metadata.getSharedCost().getNationalNumberPattern());
667     assertEquals("\\d{7}", metadata.getPersonalNumber().getNationalNumberPattern());
668     assertEquals("\\d{8}", metadata.getVoip().getNationalNumberPattern());
669     assertEquals("\\d{9}", metadata.getUan().getNationalNumberPattern());
670   }
671 
testSetRelevantDescPatternsSetsAllDescriptionsForShortNumbers()672   public void testSetRelevantDescPatternsSetsAllDescriptionsForShortNumbers()
673       throws ParserConfigurationException, SAXException, IOException {
674     String xmlInput = "<territory ID=\"FR\">"
675         + "  <tollFree><nationalNumberPattern>\\d{1}</nationalNumberPattern></tollFree>"
676         + "  <standardRate><nationalNumberPattern>\\d{2}</nationalNumberPattern></standardRate>"
677         + "  <premiumRate><nationalNumberPattern>\\d{3}</nationalNumberPattern></premiumRate>"
678         + "  <shortCode><nationalNumberPattern>\\d{4}</nationalNumberPattern></shortCode>"
679         + "  <carrierSpecific>"
680         + "    <nationalNumberPattern>\\d{5}</nationalNumberPattern>"
681         + "  </carrierSpecific>"
682         + "  <smsServices>"
683         + "    <nationalNumberPattern>\\d{6}</nationalNumberPattern>"
684         + "  </smsServices>"
685         + "</territory>";
686     Element territoryElement = parseXmlString(xmlInput);
687     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
688     BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
689         true /* isShortNumberMetadata */);
690     assertEquals("\\d{1}", metadata.getTollFree().getNationalNumberPattern());
691     assertEquals("\\d{2}", metadata.getStandardRate().getNationalNumberPattern());
692     assertEquals("\\d{3}", metadata.getPremiumRate().getNationalNumberPattern());
693     assertEquals("\\d{4}", metadata.getShortCode().getNationalNumberPattern());
694     assertEquals("\\d{5}", metadata.getCarrierSpecific().getNationalNumberPattern());
695     assertEquals("\\d{6}", metadata.getSmsServices().getNationalNumberPattern());
696   }
697 
testSetRelevantDescPatternsThrowsErrorIfTypePresentMultipleTimes()698   public void testSetRelevantDescPatternsThrowsErrorIfTypePresentMultipleTimes()
699       throws ParserConfigurationException, SAXException, IOException {
700     String xmlInput = "<territory countryCode=\"33\">"
701         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
702         + "  <fixedLine><nationalNumberPattern>\\d{6}</nationalNumberPattern></fixedLine>"
703         + "</territory>";
704     Element territoryElement = parseXmlString(xmlInput);
705     PhoneMetadata.Builder metadata = PhoneMetadata.newBuilder();
706     try {
707       BuildMetadataFromXml.setRelevantDescPatterns(metadata, territoryElement,
708           false /* isShortNumberMetadata */);
709       fail("Fixed-line info present twice for France: we should fail.");
710     } catch (RuntimeException expected) {
711       assertEquals("Multiple elements with type fixedLine found.", expected.getMessage());
712     }
713   }
714 
testAlternateFormatsOmitsDescPatterns()715   public void testAlternateFormatsOmitsDescPatterns()
716       throws ParserConfigurationException, SAXException, IOException {
717     String xmlInput = "<territory countryCode=\"33\">"
718         + "  <availableFormats>"
719         + "    <numberFormat pattern=\"(1)(\\d{3})\">"
720         + "      <leadingDigits>1</leadingDigits>"
721         + "      <format>$1</format>"
722         + "    </numberFormat>"
723         + "  </availableFormats>"
724         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
725         + "  <shortCode><nationalNumberPattern>\\d{2}</nationalNumberPattern></shortCode>"
726         + "</territory>";
727     Element territoryElement = parseXmlString(xmlInput);
728     PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
729         false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build();
730     assertEquals("(1)(\\d{3})", metadata.getNumberFormat(0).getPattern());
731     assertEquals("1", metadata.getNumberFormat(0).getLeadingDigitsPattern(0));
732     assertEquals("$1", metadata.getNumberFormat(0).getFormat());
733     assertFalse(metadata.hasFixedLine());
734     assertNull(metadata.getFixedLine());
735     assertFalse(metadata.hasShortCode());
736     assertNull(metadata.getShortCode());
737   }
738 
testNationalPrefixRulesSetCorrectly()739   public void testNationalPrefixRulesSetCorrectly()
740       throws ParserConfigurationException, SAXException, IOException {
741     String xmlInput = "<territory countryCode=\"33\" nationalPrefix=\"0\""
742         + " nationalPrefixFormattingRule=\"$NP$FG\">"
743         + "  <availableFormats>"
744         + "    <numberFormat pattern=\"(1)(\\d{3})\" nationalPrefixOptionalWhenFormatting=\"true\">"
745         + "      <leadingDigits>1</leadingDigits>"
746         + "      <format>$1</format>"
747         + "    </numberFormat>"
748         + "    <numberFormat pattern=\"(\\d{3})\" nationalPrefixOptionalWhenFormatting=\"false\">"
749         + "      <leadingDigits>2</leadingDigits>"
750         + "      <format>$1</format>"
751         + "    </numberFormat>"
752         + "  </availableFormats>"
753         + "  <fixedLine><nationalNumberPattern>\\d{1}</nationalNumberPattern></fixedLine>"
754         + "</territory>";
755     Element territoryElement = parseXmlString(xmlInput);
756     PhoneMetadata metadata = BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
757         false /* isShortNumberMetadata */, true /* isAlternateFormatsMetadata */).build();
758     assertTrue(metadata.getNumberFormat(0).getNationalPrefixOptionalWhenFormatting());
759     // This is inherited from the territory, with $NP replaced by the actual national prefix, and
760     // $FG replaced with $1.
761     assertEquals("0$1", metadata.getNumberFormat(0).getNationalPrefixFormattingRule());
762     // Here it is explicitly set to false.
763     assertFalse(metadata.getNumberFormat(1).getNationalPrefixOptionalWhenFormatting());
764   }
765 
testProcessPhoneNumberDescElement_PossibleLengthsSetCorrectly()766   public void testProcessPhoneNumberDescElement_PossibleLengthsSetCorrectly() throws Exception {
767     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
768     // The number lengths set for the general description must be a super-set of those in the
769     // element being parsed.
770     generalDesc.addPossibleLength(4);
771     generalDesc.addPossibleLength(6);
772     generalDesc.addPossibleLength(7);
773     generalDesc.addPossibleLength(13);
774     Element territoryElement = parseXmlString("<territory>"
775         + "<fixedLine>"
776         // Sorting will be done when parsing.
777         + "  <possibleLengths national=\"13,4\" localOnly=\"6\"/>"
778         + "</fixedLine>"
779         + "</territory>");
780 
781     PhoneNumberDesc.Builder fixedLine;
782     PhoneNumberDesc.Builder mobile;
783 
784     fixedLine = BuildMetadataFromXml.processPhoneNumberDescElement(
785         generalDesc, territoryElement, "fixedLine");
786     mobile = BuildMetadataFromXml.processPhoneNumberDescElement(
787         generalDesc, territoryElement, "mobile");
788 
789     assertEquals(2, fixedLine.getPossibleLengthCount());
790     assertEquals(4, fixedLine.getPossibleLength(0));
791     assertEquals(13, fixedLine.getPossibleLength(1));
792     assertEquals(1, fixedLine.getPossibleLengthLocalOnlyCount());
793 
794     // We use [-1] to denote that there are no possible lengths; we don't leave it empty, since for
795     // compression reasons, we use the empty list to mean that the generalDesc possible lengths
796     // apply.
797     assertEquals(1, mobile.getPossibleLengthCount());
798     assertEquals(-1, mobile.getPossibleLength(0));
799     assertEquals(0, mobile.getPossibleLengthLocalOnlyCount());
800   }
801 
testSetPossibleLengthsGeneralDesc_BuiltFromChildElements()802   public void testSetPossibleLengthsGeneralDesc_BuiltFromChildElements() throws Exception {
803     Element territoryElement = parseXmlString("<territory>"
804         + "<fixedLine>"
805         + "  <possibleLengths national=\"13\" localOnly=\"6\"/>"
806         + "</fixedLine>"
807         + "<mobile>"
808         + "  <possibleLengths national=\"15\" localOnly=\"7,13\"/>"
809         + "</mobile>"
810         + "<tollFree>"
811         + "  <possibleLengths national=\"15\"/>"
812         + "</tollFree>"
813         + "</territory>");
814     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
815     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
816         generalDesc, "someId", territoryElement, false /* not short-number metadata */);
817 
818     assertEquals(2, generalDesc.getPossibleLengthCount());
819     assertEquals(13, generalDesc.getPossibleLength(0));
820     // 15 is present twice in the input in different sections, but only once in the output.
821     assertEquals(15, generalDesc.getPossibleLength(1));
822     assertEquals(2, generalDesc.getPossibleLengthLocalOnlyCount());
823     assertEquals(6, generalDesc.getPossibleLengthLocalOnly(0));
824     assertEquals(7, generalDesc.getPossibleLengthLocalOnly(1));
825     // 13 is skipped as a "local only" length, since it is also present as a normal length.
826   }
827 
testSetPossibleLengthsGeneralDesc_IgnoresNoIntlDialling()828   public void testSetPossibleLengthsGeneralDesc_IgnoresNoIntlDialling() throws Exception {
829     Element territoryElement = parseXmlString("<territory>"
830         + "<fixedLine>"
831         + "  <possibleLengths national=\"13\"/>"
832         + "</fixedLine>"
833         + "<noInternationalDialling>"
834         + "  <possibleLengths national=\"15\"/>"
835         + "</noInternationalDialling>"
836         + "</territory>");
837     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
838     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
839         generalDesc, "someId", territoryElement, false /* not short-number metadata */);
840 
841     assertEquals(1, generalDesc.getPossibleLengthCount());
842     assertEquals(13, generalDesc.getPossibleLength(0));
843     // 15 is skipped because noInternationalDialling should not contribute to the general lengths;
844     // it isn't a particular "type" of number per se, it is a property that different types may
845     // have.
846   }
847 
testSetPossibleLengthsGeneralDesc_ShortNumberMetadata()848   public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadata() throws Exception {
849     Element territoryElement = parseXmlString("<territory>"
850         + "<shortCode>"
851         + "  <possibleLengths national=\"6,13\"/>"
852         + "</shortCode>"
853         + "<carrierSpecific>"
854         + "  <possibleLengths national=\"7,13,15\"/>"
855         + "</carrierSpecific>"
856         + "<tollFree>"
857         + "  <possibleLengths national=\"15\"/>"
858         + "</tollFree>"
859         + "<smsServices>"
860         + "  <possibleLengths national=\"5\"/>"
861         + "</smsServices>"
862         + "</territory>");
863     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
864     BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
865         generalDesc, "someId", territoryElement, true /* short-number metadata */);
866 
867     // All elements other than shortCode are ignored when creating the general desc.
868     assertEquals(2, generalDesc.getPossibleLengthCount());
869     assertEquals(6, generalDesc.getPossibleLength(0));
870     assertEquals(13, generalDesc.getPossibleLength(1));
871   }
872 
testSetPossibleLengthsGeneralDesc_ShortNumberMetadataErrorsOnLocalLengths()873   public void testSetPossibleLengthsGeneralDesc_ShortNumberMetadataErrorsOnLocalLengths()
874       throws Exception {
875     Element territoryElement = parseXmlString("<territory>"
876         + "<shortCode>"
877         + "  <possibleLengths national=\"13\" localOnly=\"6\"/>"
878         + "</shortCode>"
879         + "</territory>");
880 
881     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
882     try {
883       BuildMetadataFromXml.setPossibleLengthsGeneralDesc(
884           generalDesc, "someId", territoryElement, true /* short-number metadata */);
885       fail();
886     } catch (RuntimeException expected) {
887       // This should be an error, localOnly is not permitted in short-code metadata.
888       assertEquals("Found local-only lengths in short-number metadata", expected.getMessage());
889     }
890   }
891 
testProcessPhoneNumberDescElement_ErrorDuplicates()892   public void testProcessPhoneNumberDescElement_ErrorDuplicates() throws Exception {
893     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
894     generalDesc.addPossibleLength(6);
895 
896     Element territoryElement = parseXmlString("<territory>"
897         + "<mobile>"
898         + "  <possibleLengths national=\"6,6\"/>"
899         + "</mobile>"
900         + "</territory>");
901 
902     try {
903       BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile");
904       fail("Invalid data seen: expected failure.");
905     } catch (RuntimeException expected) {
906       // This should be an error, 6 is seen twice.
907       assertEquals("Duplicate length element found (6) in possibleLength string 6,6",
908           expected.getMessage());
909     }
910   }
911 
testProcessPhoneNumberDescElement_ErrorDuplicatesOneLocal()912   public void testProcessPhoneNumberDescElement_ErrorDuplicatesOneLocal() throws Exception {
913     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
914     generalDesc.addPossibleLength(6);
915 
916     Element territoryElement = parseXmlString("<territory>"
917         + "<mobile>"
918         + "  <possibleLengths national=\"6\" localOnly=\"6\"/>"
919         + "</mobile>"
920         + "</territory>");
921 
922     try {
923       BuildMetadataFromXml.processPhoneNumberDescElement(generalDesc, territoryElement, "mobile");
924       fail("Invalid data seen: expected failure.");
925     } catch (RuntimeException expected) {
926       // This should be an error, 6 is seen twice.
927       assertEquals("Possible length(s) found specified as a normal and local-only length: [6]",
928           expected.getMessage());
929     }
930   }
931 
testProcessPhoneNumberDescElement_ErrorUncoveredLengths()932   public void testProcessPhoneNumberDescElement_ErrorUncoveredLengths() throws Exception {
933     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
934     generalDesc.addPossibleLength(4);
935     Element territoryElement = parseXmlString("<territory>"
936         + "<noInternationalDialling>"
937         // Sorting will be done when parsing.
938         + "  <possibleLengths national=\"6,7,4\"/>"
939         + "</noInternationalDialling>"
940         + "</territory>");
941     try {
942       BuildMetadataFromXml.processPhoneNumberDescElement(
943           generalDesc, territoryElement, "noInternationalDialling");
944       fail("Lengths present not covered by the general desc: should fail.");
945     } catch (RuntimeException expected) {
946       // Lengths were present that the general description didn't know about.
947       assertTrue(expected.getMessage().contains("Out-of-range possible length"));
948     }
949   }
950 
testProcessPhoneNumberDescElement_SameAsParent()951   public void testProcessPhoneNumberDescElement_SameAsParent() throws Exception {
952     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
953     // The number lengths set for the general description must be a super-set of those in the
954     // element being parsed.
955     generalDesc.addPossibleLength(4);
956     generalDesc.addPossibleLength(6);
957     generalDesc.addPossibleLength(7);
958     generalDesc.addPossibleLengthLocalOnly(2);
959     Element territoryElement = parseXmlString("<territory>"
960         + "<fixedLine>"
961         // Sorting will be done when parsing.
962         + "  <possibleLengths national=\"6,7,4\" localOnly=\"2\"/>"
963         + "</fixedLine>"
964         + "</territory>");
965 
966     PhoneNumberDesc.Builder phoneNumberDesc = BuildMetadataFromXml.processPhoneNumberDescElement(
967         generalDesc, territoryElement, "fixedLine");
968     // No possible lengths should be present, because they match the general description.
969     assertEquals(0, phoneNumberDesc.getPossibleLengthCount());
970     // Local-only lengths should be present for child elements such as fixed-line.
971     assertEquals(1, phoneNumberDesc.getPossibleLengthLocalOnlyCount());
972   }
973 
testProcessPhoneNumberDescElement_InvalidNumber()974   public void testProcessPhoneNumberDescElement_InvalidNumber() throws Exception {
975     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
976     generalDesc.addPossibleLength(4);
977     Element territoryElement = parseXmlString("<territory>"
978         + "<fixedLine>"
979         + "  <possibleLengths national=\"4d\"/>"
980         + "</fixedLine>"
981         + "</territory>");
982 
983     try {
984       BuildMetadataFromXml.processPhoneNumberDescElement(
985           generalDesc, territoryElement, "fixedLine");
986       fail("4d is not a number.");
987     } catch (NumberFormatException expected) {
988       assertEquals("For input string: \"4d\"", expected.getMessage());
989     }
990   }
991 
testLoadCountryMetadata_GeneralDescHasNumberLengthsSet()992   public void testLoadCountryMetadata_GeneralDescHasNumberLengthsSet() throws Exception {
993     Element territoryElement = parseXmlString("<territory>"
994         + "<generalDesc>"
995         // This shouldn't be set, the possible lengths should be derived for generalDesc.
996         + "  <possibleLengths national=\"4\"/>"
997         + "</generalDesc>"
998         + "<fixedLine>"
999         + "  <possibleLengths national=\"4\"/>"
1000         + "</fixedLine>"
1001         + "</territory>");
1002 
1003     try {
1004       BuildMetadataFromXml.loadCountryMetadata("FR", territoryElement,
1005           false /* isShortNumberMetadata */, false /* isAlternateFormatsMetadata */);
1006       fail("Possible lengths explicitly set for generalDesc and should not be: we should fail.");
1007     } catch (RuntimeException expected) {
1008       assertEquals("Found possible lengths specified at general desc: this should be derived"
1009           + " from child elements. Affected country: FR", expected.getMessage());
1010     }
1011   }
1012 
testProcessPhoneNumberDescElement_ErrorEmptyPossibleLengthStringAttribute()1013   public void testProcessPhoneNumberDescElement_ErrorEmptyPossibleLengthStringAttribute()
1014       throws Exception {
1015     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1016     generalDesc.addPossibleLength(4);
1017     Element territoryElement = parseXmlString("<territory>"
1018         + "<fixedLine>"
1019         + "  <possibleLengths national=\"\"/>"
1020         + "</fixedLine>"
1021         + "</territory>");
1022     try {
1023       BuildMetadataFromXml.processPhoneNumberDescElement(
1024           generalDesc, territoryElement, "fixedLine");
1025       fail("Empty possible length string.");
1026     } catch (RuntimeException expected) {
1027       assertEquals("Empty possibleLength string found.", expected.getMessage());
1028     }
1029   }
1030 
testProcessPhoneNumberDescElement_ErrorRangeSpecifiedWithComma()1031   public void testProcessPhoneNumberDescElement_ErrorRangeSpecifiedWithComma()
1032       throws Exception {
1033     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1034     generalDesc.addPossibleLength(4);
1035     Element territoryElement = parseXmlString("<territory>"
1036         + "<fixedLine>"
1037         + "  <possibleLengths national=\"[4,7]\"/>"
1038         + "</fixedLine>"
1039         + "</territory>");
1040     try {
1041       BuildMetadataFromXml.processPhoneNumberDescElement(
1042           generalDesc, territoryElement, "fixedLine");
1043       fail("Ranges shouldn't use a comma.");
1044     } catch (RuntimeException expected) {
1045       assertEquals("Missing end of range character in possible length string [4,7].",
1046           expected.getMessage());
1047     }
1048   }
1049 
testProcessPhoneNumberDescElement_ErrorIncompleteRange()1050   public void testProcessPhoneNumberDescElement_ErrorIncompleteRange() throws Exception {
1051     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1052     generalDesc.addPossibleLength(4);
1053     Element territoryElement = parseXmlString("<territory>"
1054         + "<fixedLine>"
1055         + "  <possibleLengths national=\"[4-\"/>"
1056         + "</fixedLine>"
1057         + "</territory>");
1058 
1059     try {
1060       BuildMetadataFromXml.processPhoneNumberDescElement(
1061           generalDesc, territoryElement, "fixedLine");
1062       fail("Should fail: range incomplete.");
1063     } catch (RuntimeException expected) {
1064       assertEquals("Missing end of range character in possible length string [4-.",
1065           expected.getMessage());
1066     }
1067   }
1068 
testProcessPhoneNumberDescElement_ErrorNoDashInRange()1069   public void testProcessPhoneNumberDescElement_ErrorNoDashInRange() throws Exception {
1070     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1071     generalDesc.addPossibleLength(4);
1072     Element territoryElement = parseXmlString("<territory>"
1073         + "<fixedLine>"
1074         + "  <possibleLengths national=\"[4:10]\"/>"
1075         + "</fixedLine>"
1076         + "</territory>");
1077 
1078     try {
1079       BuildMetadataFromXml.processPhoneNumberDescElement(
1080           generalDesc, territoryElement, "fixedLine");
1081       fail("Should fail: range incomplete.");
1082     } catch (RuntimeException expected) {
1083       assertEquals("Ranges must have exactly one - character: missing for [4:10].",
1084           expected.getMessage());
1085     }
1086   }
1087 
testProcessPhoneNumberDescElement_ErrorRangeIsNotFromMinToMax()1088   public void testProcessPhoneNumberDescElement_ErrorRangeIsNotFromMinToMax() throws Exception {
1089     PhoneNumberDesc.Builder generalDesc = PhoneNumberDesc.newBuilder();
1090     generalDesc.addPossibleLength(4);
1091     Element territoryElement = parseXmlString("<territory>"
1092         + "<fixedLine>"
1093         + "  <possibleLengths national=\"[10-10]\"/>"
1094         + "</fixedLine>"
1095         + "</territory>");
1096 
1097     try {
1098       BuildMetadataFromXml.processPhoneNumberDescElement(
1099           generalDesc, territoryElement, "fixedLine");
1100       fail("Should fail: range even.");
1101     } catch (RuntimeException expected) {
1102       assertEquals("The first number in a range should be two or more digits lower than the second."
1103           + " Culprit possibleLength string: [10-10]", expected.getMessage());
1104     }
1105   }
1106 
testGetMetadataFilter()1107   public void testGetMetadataFilter() {
1108     assertEquals(BuildMetadataFromXml.getMetadataFilter(false, false),
1109         MetadataFilter.emptyFilter());
1110     assertEquals(BuildMetadataFromXml.getMetadataFilter(true, false),
1111         MetadataFilter.forLiteBuild());
1112     assertEquals(BuildMetadataFromXml.getMetadataFilter(false, true),
1113         MetadataFilter.forSpecialBuild());
1114     try {
1115       BuildMetadataFromXml.getMetadataFilter(true, true);
1116       fail("getMetadataFilter should fail when liteBuild and specialBuild are both set");
1117     } catch (RuntimeException e) {
1118       // Test passed.
1119     }
1120   }
1121 }
1122