Completed
Push — master ( dd9cf2...939550 )
by Joshua
13s
created

ExampleNumbersTest::testVoicemail()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace libphonenumber\Tests\core;
4
5
use libphonenumber\NumberParseException;
6
use libphonenumber\PhoneNumberFormat;
7
use libphonenumber\PhoneNumberType;
8
use libphonenumber\PhoneNumberUtil;
9
use libphonenumber\ShortNumberCost;
10
use libphonenumber\ShortNumberInfo;
11
12
/**
13
 * Verifies all of the example numbers in the metadata are valid and of the correct type. If no
14
 * example number exists for a particular type, the test still passes since not all types are
15
 * relevant for all regions. Tests that check the XML schema will ensure that an exampleNumber
16
 * node is present for every phone number description.
17
 */
18
class ExampleNumbersTest extends \PHPUnit_Framework_TestCase
19
{
20
21
    /**
22
     * @var PhoneNumberUtil
23
     */
24
    private $phoneNumberUtil;
25
    /**
26
     * @var ShortNumberInfo
27
     */
28
    private $shortNumberInfo;
29
30
    public static function setUpBeforeClass()
31
    {
32
        PhoneNumberUtil::resetInstance();
33
        PhoneNumberUtil::getInstance();
34
        ShortNumberInfo::resetInstance();
35
    }
36
37
    public function setUp()
38
    {
39
        $this->phoneNumberUtil = PhoneNumberUtil::getInstance();
40
        $this->shortNumberInfo = ShortNumberInfo::getInstance();
41
    }
42
43
    public function regionList()
44
    {
45
        $returnList = array();
46
47
        PhoneNumberUtil::resetInstance();
48
        $phoneUtil = PhoneNumberUtil::getInstance();
49
        foreach ($phoneUtil->getSupportedRegions() as $regionCode) {
50
            $returnList[] = array($regionCode);
51
        }
52
53
        return $returnList;
54
    }
55
56
    public function numberTypes()
57
    {
58
        return array(
59
            array(PhoneNumberType::FIXED_LINE),
60
            array(PhoneNumberType::MOBILE),
61
            array(PhoneNumberType::FIXED_LINE_OR_MOBILE),
62
            array(PhoneNumberType::TOLL_FREE),
63
            array(PhoneNumberType::PREMIUM_RATE),
64
            array(PhoneNumberType::SHARED_COST),
65
            array(PhoneNumberType::VOIP),
66
            array(PhoneNumberType::PERSONAL_NUMBER),
67
            array(PhoneNumberType::PAGER),
68
            array(PhoneNumberType::UAN),
69
            array(PhoneNumberType::VOICEMAIL),
70
        );
71
    }
72
73
    /**
74
     * @dataProvider regionList
75
     */
76
    public function testFixedLine($region)
77
    {
78
        $fixedLineTypes = array(PhoneNumberType::FIXED_LINE, PhoneNumberType::FIXED_LINE_OR_MOBILE);
79
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::FIXED_LINE, $fixedLineTypes, $region);
80
    }
81
82
    /**
83
     * @dataProvider regionList
84
     */
85
    public function testFixedLineOrMobile($region)
86
    {
87
        $numberTypes = array(PhoneNumberType::FIXED_LINE, PhoneNumberType::FIXED_LINE_OR_MOBILE);
88
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::FIXED_LINE_OR_MOBILE, $numberTypes, $region);
89
    }
90
91
    private function checkNumbersValidAndCorrectType($exampleNumberRequestedType, $possibleExpectedTypes, $regionCode)
92
    {
93
        $exampleNumber = $this->phoneNumberUtil->getExampleNumberForType($regionCode, $exampleNumberRequestedType);
94
        if ($exampleNumber !== null) {
95
            $this->assertTrue(
96
                $this->phoneNumberUtil->isValidNumber($exampleNumber),
97
                "Failed validation for {$exampleNumber}"
98
            );
99
100
            // We know the number is valid, now we check the type.
101
            $exampleNumberType = $this->phoneNumberUtil->getNumberType($exampleNumber);
102
            $this->assertContains($exampleNumberType, $possibleExpectedTypes, "Wrong type for {$exampleNumber}");
103
        }
104
    }
105
106
    /**
107
     * @dataProvider regionList
108
     */
109
    public function testMobile($region)
110
    {
111
        $mobileTypes = array(PhoneNumberType::MOBILE, PhoneNumberType::FIXED_LINE_OR_MOBILE);
112
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::MOBILE, $mobileTypes, $region);
113
    }
114
115
    /**
116
     * @dataProvider regionList
117
     */
118
    public function testTollFree($region)
119
    {
120
        $tollFreeTypes = array(PhoneNumberType::TOLL_FREE);
121
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::TOLL_FREE, $tollFreeTypes, $region);
122
    }
123
124
    /**
125
     * @dataProvider regionList
126
     */
127
    public function testPremiumRate($region)
128
    {
129
        $premiumRateTypes = array(PhoneNumberType::PREMIUM_RATE);
130
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::PREMIUM_RATE, $premiumRateTypes, $region);
131
    }
132
133
    /**
134
     * @dataProvider regionList
135
     */
136
    public function testVoip($region)
137
    {
138
        $voipTypes = array(PhoneNumberType::VOIP);
139
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::VOIP, $voipTypes, $region);
140
    }
141
142
    /**
143
     * @dataProvider regionList
144
     */
145
    public function testPager($region)
146
    {
147
        $pagerTypes = array(PhoneNumberType::PAGER);
148
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::PAGER, $pagerTypes, $region);
149
    }
150
151
    /**
152
     * @dataProvider regionList
153
     */
154
    public function testUan($region)
155
    {
156
        $uanTypes = array(PhoneNumberType::UAN);
157
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::UAN, $uanTypes, $region);
158
    }
159
160
    /**
161
     * @dataProvider regionList
162
     */
163
    public function testVoicemail($region)
164
    {
165
        $voicemailTypes = array(PhoneNumberType::VOICEMAIL);
166
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::VOICEMAIL, $voicemailTypes, $region);
167
    }
168
169
    /**
170
     * @dataProvider regionList
171
     */
172
    public function testPersonalNumber($region)
173
    {
174
        $numberTypes = array(PhoneNumberType::PERSONAL_NUMBER);
175
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::PERSONAL_NUMBER, $numberTypes, $region);
176
    }
177
178
    /**
179
     * @dataProvider regionList
180
     */
181
    public function testSharedCost($region)
182
    {
183
        $sharedCostTypes = array(PhoneNumberType::SHARED_COST);
184
        $this->checkNumbersValidAndCorrectType(PhoneNumberType::SHARED_COST, $sharedCostTypes, $region);
185
    }
186
187
    /**
188
     * @dataProvider regionList
189
     */
190
    public function testCanBeInternationallyDialled($regionCode)
191
    {
192
        $exampleNumber = null;
193
        /** @var \libphonenumber\PhoneNumberDesc $desc */
194
        $desc = $this->phoneNumberUtil->getMetadataForRegion($regionCode)->getNoInternationalDialling();
195
        try {
196
            if ($desc->hasExampleNumber()) {
197
                $exampleNumber = $this->phoneNumberUtil->parse($desc->getExampleNumber(), $regionCode);
198
            }
199
        } catch (NumberParseException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
200
        }
201
202
        if ($exampleNumber !== null && $this->phoneNumberUtil->canBeInternationallyDialled($exampleNumber)) {
203
            $this->fail("Number {$exampleNumber} should not be internationally diallable");
204
        }
205
    }
206
207
    public function shortNumberRegionList()
208
    {
209
        $returnList = array();
210
211
        PhoneNumberUtil::resetInstance();
212
        ShortNumberInfo::resetInstance();
213
        $shortNumberInfo = ShortNumberInfo::getInstance();
214
        foreach ($shortNumberInfo->getSupportedRegions() as $regionCode) {
215
            $returnList[] = array($regionCode);
216
        }
217
218
        return $returnList;
219
    }
220
221
    public function supportedGlobalNetworkCallingCodes()
222
    {
223
        $returnList = array();
224
225
        PhoneNumberUtil::resetInstance();
226
        $phoneUtil = PhoneNumberUtil::getInstance();
227
        foreach ($phoneUtil->getSupportedGlobalNetworkCallingCodes() as $callingCode) {
228
            $returnList[] = array($callingCode);
229
        }
230
231
        return $returnList;
232
    }
233
234
    /**
235
     * @dataProvider supportedGlobalNetworkCallingCodes
236
     */
237
    public function testGlobalNetworkNumbers($callingCode)
238
    {
239
        $exampleNumber = $this->phoneNumberUtil->getExampleNumberForNonGeoEntity($callingCode);
240
        $this->assertNotNull($exampleNumber, "No example phone number for calling code " . $callingCode);
241
        if (!$this->phoneNumberUtil->isValidNumber($exampleNumber)) {
0 ignored issues
show
Bug introduced by
It seems like $exampleNumber defined by $this->phoneNumberUtil->...GeoEntity($callingCode) on line 239 can be null; however, libphonenumber\PhoneNumberUtil::isValidNumber() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
242
            $this->fail("Failed validation for " . $exampleNumber);
243
        }
244
    }
245
246
    /**
247
     * @dataProvider regionList
248
     * @param string $regionCode
249
     */
250
    public function testEveryRegionHasAnExampleNumber($regionCode)
251
    {
252
        $exampleNumber = $this->phoneNumberUtil->getExampleNumber($regionCode);
253
        $this->assertNotNull($exampleNumber, "No example number found for region " . $regionCode);
254
255
        /*
256
         * Check the number is valid
257
         */
258
259
        $e164 = $this->phoneNumberUtil->format($exampleNumber, PhoneNumberFormat::E164);
0 ignored issues
show
Bug introduced by
It seems like $exampleNumber defined by $this->phoneNumberUtil->...mpleNumber($regionCode) on line 252 can be null; however, libphonenumber\PhoneNumberUtil::format() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
260
261
        $phoneObject = $this->phoneNumberUtil->parse($e164, 'ZZ');
262
263
        $this->assertEquals($phoneObject, $exampleNumber);
264
265
        $this->assertTrue($this->phoneNumberUtil->isValidNumber($phoneObject));
266
        $this->assertTrue($this->phoneNumberUtil->isValidNumberForRegion($phoneObject, $regionCode));
267
    }
268
269
    /**
270
     * @dataProvider regionList
271
     * @param string $regionCode
272
     */
273
    public function testEveryRegionHasAnInvalidExampleNumber($regionCode)
274
    {
275
        $exampleNumber = $this->phoneNumberUtil->getInvalidExampleNumber($regionCode);
276
        $this->assertNotNull($exampleNumber, 'No invalid example number found for region ' . $regionCode);
277
    }
278
279
    /**
280
     * @dataProvider numberTypes
281
     * @param string $numberType
282
     */
283
    public function testEveryTypeHasAnExampleNumber($numberType)
284
    {
285
        $exampleNumber = $this->phoneNumberUtil->getExampleNumberForType($numberType);
286
        $this->assertNotNull($exampleNumber, 'No example number found for type ' . $numberType);
287
    }
288
289
    /**
290
     * @dataProvider shortNumberRegionList
291
     */
292
    public function testShortNumbersValidAndCorrectCost($regionCode)
293
    {
294
        $exampleShortNumber = $this->shortNumberInfo->getExampleShortNumber($regionCode);
295
        if (!$this->shortNumberInfo->isValidShortNumberForRegion(
296
            $this->phoneNumberUtil->parse($exampleShortNumber, $regionCode),
297
            $regionCode
298
        )
299
        ) {
300
            $this->fail(
301
                "Failed validation for string region_code: {$regionCode}, national_number: {$exampleShortNumber}"
302
            );
303
        }
304
        $phoneNumber = $this->phoneNumberUtil->parse($exampleShortNumber, $regionCode);
305
        if (!$this->shortNumberInfo->isValidShortNumber($phoneNumber)) {
306
            $this->fail("Failed validation for " . (string)$phoneNumber);
307
        }
308
    }
309
310
    public function shortRegionListAndNumberCost()
311
    {
312
        $costArray = array(
313
            ShortNumberCost::PREMIUM_RATE,
314
            ShortNumberCost::STANDARD_RATE,
315
            ShortNumberCost::TOLL_FREE,
316
            ShortNumberCost::UNKNOWN_COST
317
        );
318
319
        $output = array();
320
321
        foreach ($this->shortNumberRegionList() as $region) {
322
            foreach ($costArray as $cost) {
323
                $output[] = array($region[0], $cost);
324
            }
325
        }
326
327
        return $output;
328
    }
329
330
    /**
331
     * @dataProvider shortRegionListAndNumberCost
332
     * @param $regionCode
333
     * @param $cost
334
     */
335
    public function testShortNumberHasCorrectCost($regionCode, $cost)
336
    {
337
        $exampleShortNumber = $this->shortNumberInfo->getExampleShortNumberForCost($regionCode, $cost);
338
        if ($exampleShortNumber != '') {
339
            $phoneNumber = $this->phoneNumberUtil->parse($exampleShortNumber, $regionCode);
340
            $exampleShortNumberCost = $this->shortNumberInfo->getExpectedCostForRegion($phoneNumber, $regionCode);
341
342
            $this->assertEquals($cost, $exampleShortNumberCost, 'Wrong cost for ' . (string)$phoneNumber);
343
        }
344
    }
345
346
    /**
347
     * @dataProvider shortNumberRegionList
348
     */
349
    public function testEmergency($regionCode)
350
    {
351
        $desc = $this->shortNumberInfo->getMetadataForRegion($regionCode)->getEmergency();
352
        if ($desc->hasExampleNumber()) {
353
            $exampleNumber = $desc->getExampleNumber();
354
            $phoneNumber = $this->phoneNumberUtil->parse($exampleNumber, $regionCode);
355
356
            if (!$this->shortNumberInfo->isPossibleShortNumberForRegion(
357
                    $phoneNumber,
358
                    $regionCode
359
                ) || !$this->shortNumberInfo->isEmergencyNumber($exampleNumber, $regionCode)
360
            ) {
361
                $this->fail("Emergency example number test failed for " . $regionCode);
362
            } elseif ($this->shortNumberInfo->getExpectedCostForRegion(
363
                    $phoneNumber,
364
                    $regionCode
365
                ) !== ShortNumberCost::TOLL_FREE
366
            ) {
367
                $this->fail("Emergency example number not toll free for " . $regionCode);
368
            }
369
        }
370
    }
371
372
    /**
373
     * @dataProvider shortNumberRegionList
374
     * @param string $regionCode
375
     */
376
    public function testCarrierSpecificShortNumbers($regionCode)
377
    {
378
        // Test the carrier-specific tag.
379
        $desc = $this->shortNumberInfo->getMetadataForRegion($regionCode)->getCarrierSpecific();
380
        if ($desc->hasExampleNumber()) {
381
            $exampleNumber = $desc->getExampleNumber();
382
            $carrierSpecificNumber = $this->phoneNumberUtil->parse($exampleNumber, $regionCode);
383
384
            if (!$this->shortNumberInfo->isPossibleShortNumberForRegion($carrierSpecificNumber, $regionCode)
385
                || !$this->shortNumberInfo->isCarrierSpecificForRegion($carrierSpecificNumber, $regionCode)
386
            ) {
387
                $this->fail("Carrier-specific test failed for " . $regionCode);
388
            }
389
        }
390
    }
391
392
    /**
393
     * @dataProvider shortNumberRegionList
394
     * @param string $regionCode
395
     */
396
    public function testSmsServiceShortNumbers($regionCode)
397
    {
398
        $desc = $this->shortNumberInfo->getMetadataForRegion($regionCode)->getSmsServices();
399
400
        if ($desc->hasExampleNumber()) {
401
            $exampleNumber = $desc->getExampleNumber();
402
            $smsServiceNumber = $this->phoneNumberUtil->parse($exampleNumber, $regionCode);
403
            if (!$this->shortNumberInfo->isPossibleShortNumberForRegion($smsServiceNumber, $regionCode)
404
                || !$this->shortNumberInfo->isSmsServiceForRegion($smsServiceNumber, $regionCode)) {
405
                $this->fail('SMS service test failed for ' . $regionCode);
406
            }
407
        }
408
    }
409
}
410