BasicsTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. /**
  4. * @license MIT License https://github.com/serbanghita/Mobile-Detect/blob/master/LICENSE.txt
  5. * @link http://mobiledetect.net
  6. */
  7. class BasicTest extends TestCase
  8. {
  9. /**
  10. * @var Mobile_Detect
  11. */
  12. protected $detect;
  13. public function testClassExists()
  14. {
  15. $this->assertTrue(class_exists('Mobile_Detect'));
  16. }
  17. public function setUp()
  18. {
  19. $this->detect = new Mobile_Detect;
  20. }
  21. public function testBasicMethods()
  22. {
  23. $this->assertNotEmpty( $this->detect->getScriptVersion() );
  24. $this->detect->setHttpHeaders(array(
  25. 'SERVER_SOFTWARE' => 'Apache/2.2.15 (Linux) Whatever/4.0 PHP/5.2.13',
  26. 'REQUEST_METHOD' => 'POST',
  27. 'HTTP_HOST' => 'home.ghita.org',
  28. 'HTTP_X_REAL_IP' => '1.2.3.4',
  29. 'HTTP_X_FORWARDED_FOR' => '1.2.3.5',
  30. 'HTTP_CONNECTION' => 'close',
  31. 'HTTP_USER_AGENT' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A523 Safari/8536.25',
  32. 'HTTP_ACCEPT' => 'text/vnd.wap.wml, application/json, text/javascript, */*; q=0.01',
  33. 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
  34. 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
  35. 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest',
  36. 'HTTP_REFERER' => 'http://mobiledetect.net',
  37. 'HTTP_PRAGMA' => 'no-cache',
  38. 'HTTP_CACHE_CONTROL' => 'no-cache',
  39. 'REMOTE_ADDR' => '11.22.33.44',
  40. 'REQUEST_TIME' => '01-10-2012 07:57'
  41. ));
  42. //12 because only 12 start with HTTP_
  43. $this->assertCount( 12, $this->detect->getHttpHeaders() );
  44. $this->assertTrue( $this->detect->checkHttpHeadersForMobile() );
  45. $this->detect->setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A523 Safari/8536.25');
  46. $this->assertNotEmpty( $this->detect->getUserAgent() );
  47. $this->assertTrue( $this->detect->isMobile() );
  48. $this->assertFalse( $this->detect->isTablet() );
  49. $this->assertTrue( $this->detect->isIphone() );
  50. $this->assertTrue( $this->detect->isiphone() );
  51. $this->assertTrue( $this->detect->isiOS() );
  52. $this->assertTrue( $this->detect->isios() );
  53. $this->assertTrue( $this->detect->is('iphone') );
  54. $this->assertTrue( $this->detect->is('ios') );
  55. }
  56. public function headersProvider()
  57. {
  58. return array(
  59. array(array(
  60. 'SERVER_SOFTWARE' => 'Apache/2.2.15 (Linux) Whatever/4.0 PHP/5.2.13',
  61. 'REQUEST_METHOD' => 'POST',
  62. 'HTTP_HOST' => 'home.ghita.org',
  63. 'HTTP_X_REAL_IP' => '1.2.3.4',
  64. 'HTTP_X_FORWARDED_FOR' => '1.2.3.5',
  65. 'HTTP_CONNECTION' => 'close',
  66. 'HTTP_USER_AGENT' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A523 Safari/8536.25',
  67. 'HTTP_ACCEPT' => 'text/vnd.wap.wml, application/json, text/javascript, */*; q=0.01',
  68. 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
  69. 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate',
  70. 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest',
  71. 'HTTP_REFERER' => 'http://mobiledetect.net',
  72. 'HTTP_PRAGMA' => 'no-cache',
  73. 'HTTP_CACHE_CONTROL' => 'no-cache',
  74. 'REMOTE_ADDR' => '11.22.33.44',
  75. 'REQUEST_TIME' => '01-10-2012 07:57'
  76. )),
  77. array(array(
  78. 'SERVER_SOFTWARE' => 'Rogue software',
  79. 'REQUEST_METHOD' => 'GET',
  80. 'REMOTE_ADDR' => '8.8.8.8',
  81. 'REQUEST_TIME' => '07-10-2013 23:56',
  82. 'HTTP_USER_AGENT' => "garbage/1.0"
  83. )),
  84. array(array(
  85. 'SERVER_SOFTWARE' => 'Apache/1.3.17 (Linux) PHP/5.5.2',
  86. 'REQUEST_METHOD' => 'HEAD',
  87. 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Linux; U; Android 1.5; en-us; ADR6200 Build/CUPCAKE) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1',
  88. 'REMOTE_ADDR' => '1.250.250.0',
  89. 'REQUEST_TIME' => '06-12-2006 11:06'
  90. )),
  91. );
  92. }
  93. /**
  94. * @dataProvider headersProvider
  95. * @param array $headers
  96. */
  97. public function testConstructorInjection(array $headers)
  98. {
  99. $md = new Mobile_Detect($headers);
  100. foreach ($headers as $header => $value) {
  101. if (substr($header, 0, 5) !== 'HTTP_') {
  102. //make sure it wasn't set
  103. $this->assertNull($md->getHttpHeader($value));
  104. } else {
  105. //make sure it's equal
  106. $this->assertEquals($value, $md->getHttpHeader($header));
  107. }
  108. }
  109. //verify some of the headers work with the translated getter
  110. $this->assertNull($md->getHttpHeader('Remote-Addr'));
  111. $this->assertNull($md->getHttpHeader('Server-Software'));
  112. $this->assertEquals($headers['HTTP_USER_AGENT'], $md->getHttpHeader('User-Agent'));
  113. }
  114. /**
  115. * @dataProvider headersProvider
  116. * @param $headers
  117. */
  118. public function testInvalidHeader($headers)
  119. {
  120. $md = new Mobile_Detect($headers);
  121. $this->assertNull($md->getHttpHeader('garbage_is_Garbage'));
  122. }
  123. public function userAgentProvider()
  124. {
  125. return array(
  126. array(array(
  127. 'HTTP_USER_AGENT' => 'blah'
  128. ), 'blah'),
  129. array(array(
  130. 'HTTP_USER_AGENT' => 'iphone',
  131. 'HTTP_X_OPERAMINI_PHONE_UA' => 'some other stuff'
  132. ), 'iphone some other stuff'),
  133. array(array(
  134. 'HTTP_X_DEVICE_USER_AGENT' => 'hello world'
  135. ), 'hello world'),
  136. array(array(), null)
  137. );
  138. }
  139. /**
  140. * @dataProvider userAgentProvider
  141. * @param $headers
  142. * @param $expectedUserAgent
  143. */
  144. public function testGetUserAgent($headers, $expectedUserAgent)
  145. {
  146. $md = new Mobile_Detect($headers);
  147. $md->setUserAgent();
  148. $this->assertSame($expectedUserAgent, $md->getUserAgent());
  149. }
  150. /**
  151. * Headers should be reset when you use setHttpHeaders.
  152. * @issue #144
  153. */
  154. public function testSetHttpHeaders()
  155. {
  156. $header1 = array('HTTP_PINK_PONY' => 'I secretly love ponies >_>');
  157. $md = new Mobile_Detect($header1);
  158. $this->assertSame($md->getHttpHeaders(), $header1);
  159. $header2 = array('HTTP_FIRE_BREATHING_DRAGON' => 'yeah!');
  160. $md->setHttpHeaders($header2);
  161. $this->assertSame($md->getHttpHeaders(), $header2);
  162. }
  163. /**
  164. * Read response from cloudfront, if the cloudfront headers are detected
  165. */
  166. public function testSetCfHeaders()
  167. {
  168. // Test mobile detected
  169. $header1 = array(
  170. 'HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER' => 'false',
  171. 'HTTP_CLOUDFRONT_IS_MOBILE_VIEWER' => 'true',
  172. 'HTTP_CLOUDFRONT_IS_TABLET_VIEWER' => 'false'
  173. );
  174. $md = new Mobile_Detect($header1);
  175. $this->assertSame($md->getCfHeaders(), $header1);
  176. $this->assertSame($md->getUserAgent(), 'Amazon CloudFront');
  177. $this->assertSame($md->isTablet(), false);
  178. $this->assertSame($md->isMobile(), true);
  179. // Test neither mobile nor tablet (desktop)
  180. $header2 = array(
  181. 'HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER' => 'true',
  182. 'HTTP_CLOUDFRONT_IS_MOBILE_VIEWER' => 'false',
  183. 'HTTP_CLOUDFRONT_IS_TABLET_VIEWER' => 'false'
  184. );
  185. $md->setHttpHeaders($header2);
  186. $this->assertSame($md->getCfHeaders(), $header2);
  187. $this->assertSame($md->getUserAgent(), 'Amazon CloudFront');
  188. $this->assertSame($md->isTablet(), false);
  189. $this->assertSame($md->isMobile(), false);
  190. // Test tablet detected
  191. $header3 = array(
  192. 'HTTP_CLOUDFRONT_IS_DESKTOP_VIEWER' => 'false',
  193. 'HTTP_CLOUDFRONT_IS_MOBILE_VIEWER' => 'false',
  194. 'HTTP_CLOUDFRONT_IS_TABLET_VIEWER' => 'true'
  195. );
  196. $md->setCfHeaders($header3);
  197. $this->assertSame($md->getCfHeaders(), $header3);
  198. $this->assertSame($md->getUserAgent(), 'Amazon CloudFront');
  199. $this->assertSame($md->isTablet(), true);
  200. $this->assertSame($md->isMobile(), false);
  201. // Check if the headers are cleared
  202. $header4 = array();
  203. $md->setHttpHeaders($header4);
  204. $this->assertSame($md->getCfHeaders(), $header4);
  205. }
  206. public function testSetUserAgent()
  207. {
  208. $md = new Mobile_Detect(array());
  209. $md->setUserAgent('hello world');
  210. $this->assertSame('hello world', $md->getUserAgent());
  211. }
  212. public function testSetLongUserAgent() {
  213. $md = new Mobile_Detect();
  214. $md->setUserAgent(str_repeat("a", 501));
  215. $this->assertEquals(strlen($md->getUserAgent()), 500);
  216. }
  217. public function testSetDetectionType()
  218. {
  219. $md = new Mobile_Detect(array());
  220. $md->setDetectionType('bskdfjhs');
  221. $this->assertAttributeEquals(
  222. Mobile_Detect::DETECTION_TYPE_MOBILE,
  223. 'detectionType',
  224. $md
  225. );
  226. $md->setDetectionType();
  227. $this->assertAttributeEquals(
  228. Mobile_Detect::DETECTION_TYPE_MOBILE,
  229. 'detectionType',
  230. $md
  231. );
  232. $md->setDetectionType(Mobile_Detect::DETECTION_TYPE_MOBILE);
  233. $this->assertAttributeEquals(
  234. Mobile_Detect::DETECTION_TYPE_MOBILE,
  235. 'detectionType',
  236. $md
  237. );
  238. $md->setDetectionType(Mobile_Detect::DETECTION_TYPE_EXTENDED);
  239. $this->assertAttributeEquals(
  240. Mobile_Detect::DETECTION_TYPE_EXTENDED,
  241. 'detectionType',
  242. $md
  243. );
  244. }
  245. //special headers that give 'quick' indication that a device is mobile
  246. public function quickHeadersData()
  247. {
  248. return array(
  249. array(array(
  250. 'HTTP_ACCEPT' => 'application/json; q=0.2, application/x-obml2d; q=0.8, image/gif; q=0.99, */*'
  251. )),
  252. array(array(
  253. 'HTTP_ACCEPT' => 'text/*; q=0.1, application/vnd.rim.html'
  254. )),
  255. array(array(
  256. 'HTTP_ACCEPT' => 'text/vnd.wap.wml',
  257. )),
  258. array(array(
  259. 'HTTP_ACCEPT' => 'application/vnd.wap.xhtml+xml',
  260. )),
  261. array(array(
  262. 'HTTP_X_WAP_PROFILE' => 'hello',
  263. )),
  264. array(array(
  265. 'HTTP_X_WAP_CLIENTID' => ''
  266. )),
  267. array(array(
  268. 'HTTP_WAP_CONNECTION' => ''
  269. )),
  270. array(array(
  271. 'HTTP_PROFILE' => ''
  272. )),
  273. array(array(
  274. 'HTTP_X_OPERAMINI_PHONE_UA' => ''
  275. )),
  276. array(array(
  277. 'HTTP_X_NOKIA_GATEWAY_ID' => ''
  278. )),
  279. array(array(
  280. 'HTTP_X_ORANGE_ID' => ''
  281. )),
  282. array(array(
  283. 'HTTP_X_VODAFONE_3GPDPCONTEXT' => ''
  284. )),
  285. array(array(
  286. 'HTTP_X_HUAWEI_USERID' => ''
  287. )),
  288. array(array(
  289. 'HTTP_UA_OS' => ''
  290. )),
  291. array(array(
  292. 'HTTP_X_MOBILE_GATEWAY' => ''
  293. )),
  294. array(array(
  295. 'HTTP_X_ATT_DEVICEID' => ''
  296. )),
  297. array(array(
  298. 'HTTP_UA_CPU' => 'ARM'
  299. ))
  300. );
  301. }
  302. /**
  303. * @dataProvider quickHeadersData
  304. * @param $headers
  305. */
  306. public function testQuickHeaders($headers)
  307. {
  308. $md = new Mobile_Detect($headers);
  309. $this->assertTrue($md->checkHttpHeadersForMobile());
  310. }
  311. // Headers that are not mobile.
  312. public function quickNonMobileHeadersData()
  313. {
  314. return array(
  315. array(array(
  316. 'HTTP_UA_CPU' => 'AMD64'
  317. )),
  318. array(array(
  319. 'HTTP_UA_CPU' => 'X86'
  320. )),
  321. array(array(
  322. 'HTTP_ACCEPT' => 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01'
  323. )),
  324. array(array(
  325. 'HTTP_REQUEST_METHOD' => 'DELETE'
  326. )),
  327. array(array(
  328. 'HTTP_VIA' => '1.1 ws-proxy.stuff.co.il C0A800FA'
  329. )),
  330. );
  331. }
  332. /**
  333. * @dataProvider quickNonMobileHeadersData
  334. * @param $headers
  335. */
  336. public function testNonMobileQuickHeaders($headers)
  337. {
  338. $md = new Mobile_Detect($headers);
  339. $this->assertFalse($md->checkHttpHeadersForMobile());
  340. }
  341. /**
  342. * @expectedException BadMethodCallException
  343. */
  344. public function testBadMethodCall()
  345. {
  346. $md = new Mobile_Detect(array());
  347. $md->badmethodthatdoesntexistatall();
  348. }
  349. public function versionDataProvider()
  350. {
  351. return array(
  352. array(
  353. 'Mozilla/5.0 (Linux; Android 4.0.4; ARCHOS 80G9 Build/IMM76D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
  354. 'Android',
  355. '4.0.4',
  356. 4.04
  357. ),
  358. array(
  359. 'Mozilla/5.0 (Linux; Android 4.0.4; ARCHOS 80G9 Build/IMM76D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
  360. 'Webkit',
  361. '535.19',
  362. 535.19
  363. ),
  364. array(
  365. 'Mozilla/5.0 (Linux; Android 4.0.4; ARCHOS 80G9 Build/IMM76D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19',
  366. 'Chrome',
  367. '18.0.1025.166',
  368. 18.01025166
  369. ),
  370. array(
  371. 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9700; en-US) AppleWebKit/534.8 (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8',
  372. 'BlackBerry',
  373. '6.0.0.448',
  374. 6.00448
  375. ),
  376. array(
  377. 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9700; en-US) AppleWebKit/534.8 (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8',
  378. 'Webkit',
  379. '534.8',
  380. 534.8
  381. ),
  382. array(
  383. 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-GB) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.546 Mobile Safari/534.8+',
  384. 'BlackBerry',
  385. '6.0.0.546',
  386. 6.00546
  387. )
  388. );
  389. }
  390. /**
  391. * @dataProvider versionDataProvider
  392. */
  393. public function testVersionExtraction($userAgent, $property, $stringVersion, $floatVersion)
  394. {
  395. $md = new Mobile_Detect(array('HTTP_USER_AGENT' => $userAgent));
  396. $prop = $md->version($property);
  397. $this->assertSame($stringVersion, $prop);
  398. $prop = $md->version($property, 'float');
  399. $this->assertSame($floatVersion, $prop);
  400. //assert that garbage data is always === false
  401. $prop = $md->version('garbage input is always garbage');
  402. $this->assertFalse($prop);
  403. }
  404. public function testRules()
  405. {
  406. $md = new Mobile_Detect;
  407. $count = array_sum(array(
  408. count(Mobile_Detect::getPhoneDevices()),
  409. count(Mobile_Detect::getTabletDevices()),
  410. count(Mobile_Detect::getOperatingSystems()),
  411. count(Mobile_Detect::getBrowsers())
  412. ));
  413. $rules = $md->getRules();
  414. $this->assertCount($count, $rules);
  415. }
  416. public function testRulesExtended()
  417. {
  418. $md = new Mobile_Detect;
  419. $count = array_sum(array(
  420. count(Mobile_Detect::getPhoneDevices()),
  421. count(Mobile_Detect::getTabletDevices()),
  422. count(Mobile_Detect::getOperatingSystems()),
  423. count(Mobile_Detect::getBrowsers()),
  424. count(Mobile_Detect::getUtilities())
  425. ));
  426. $md->setDetectionType(Mobile_Detect::DETECTION_TYPE_EXTENDED);
  427. $rules = $md->getRules();
  428. $this->assertCount($count, $rules);
  429. }
  430. public function testScriptVersion()
  431. {
  432. $v = Mobile_Detect::getScriptVersion();
  433. $formatCheck = (bool)preg_match('/^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9])?$/', $v);
  434. $this->assertTrue($formatCheck, "Fails the semantic version test. The version " . var_export($v, true)
  435. . ' does not match X.Y.Z pattern');
  436. }
  437. public function crazyVersionNumbers()
  438. {
  439. return array(
  440. array('2.5.6', 2.56),
  441. array('12142.2142.412521.24.152', 12142.214241252124152),
  442. array('6_3', 6.3),
  443. array('4_7 /7 7 12_9', 4.777129),
  444. array('49', 49.0),
  445. array('2.6.x', 2.6),
  446. array('45.6.1.x.12', 45.61)
  447. );
  448. }
  449. /**
  450. * @dataProvider crazyVersionNumbers
  451. * @param $raw
  452. * @param $expected
  453. */
  454. public function testPrepareVersionNo($raw, $expected)
  455. {
  456. $md = new Mobile_Detect;
  457. $actual = $md->prepareVersionNo($raw);
  458. $this->assertSame($expected, $actual, "We expected " . var_export($raw, true) . " to convert to "
  459. . var_export($expected, true) . ', but got ' . var_export($actual, true) . ' instead');
  460. }
  461. }