Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / vendor / symfony / http-foundation / Tests / ResponseTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\HttpFoundation\Tests;
13
14 use Symfony\Component\HttpFoundation\Request;
15 use Symfony\Component\HttpFoundation\Response;
16
17 /**
18  * @group time-sensitive
19  */
20 class ResponseTest extends ResponseTestCase
21 {
22     public function testCreate()
23     {
24         $response = Response::create('foo', 301, array('Foo' => 'bar'));
25
26         $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
27         $this->assertEquals(301, $response->getStatusCode());
28         $this->assertEquals('bar', $response->headers->get('foo'));
29     }
30
31     public function testToString()
32     {
33         $response = new Response();
34         $response = explode("\r\n", $response);
35         $this->assertEquals('HTTP/1.0 200 OK', $response[0]);
36         $this->assertEquals('Cache-Control: no-cache, private', $response[1]);
37     }
38
39     public function testClone()
40     {
41         $response = new Response();
42         $responseClone = clone $response;
43         $this->assertEquals($response, $responseClone);
44     }
45
46     public function testSendHeaders()
47     {
48         $response = new Response();
49         $headers = $response->sendHeaders();
50         $this->assertObjectHasAttribute('headers', $headers);
51         $this->assertObjectHasAttribute('content', $headers);
52         $this->assertObjectHasAttribute('version', $headers);
53         $this->assertObjectHasAttribute('statusCode', $headers);
54         $this->assertObjectHasAttribute('statusText', $headers);
55         $this->assertObjectHasAttribute('charset', $headers);
56     }
57
58     public function testSend()
59     {
60         $response = new Response();
61         $responseSend = $response->send();
62         $this->assertObjectHasAttribute('headers', $responseSend);
63         $this->assertObjectHasAttribute('content', $responseSend);
64         $this->assertObjectHasAttribute('version', $responseSend);
65         $this->assertObjectHasAttribute('statusCode', $responseSend);
66         $this->assertObjectHasAttribute('statusText', $responseSend);
67         $this->assertObjectHasAttribute('charset', $responseSend);
68     }
69
70     public function testGetCharset()
71     {
72         $response = new Response();
73         $charsetOrigin = 'UTF-8';
74         $response->setCharset($charsetOrigin);
75         $charset = $response->getCharset();
76         $this->assertEquals($charsetOrigin, $charset);
77     }
78
79     public function testIsCacheable()
80     {
81         $response = new Response();
82         $this->assertFalse($response->isCacheable());
83     }
84
85     public function testIsCacheableWithErrorCode()
86     {
87         $response = new Response('', 500);
88         $this->assertFalse($response->isCacheable());
89     }
90
91     public function testIsCacheableWithNoStoreDirective()
92     {
93         $response = new Response();
94         $response->headers->set('cache-control', 'private');
95         $this->assertFalse($response->isCacheable());
96     }
97
98     public function testIsCacheableWithSetTtl()
99     {
100         $response = new Response();
101         $response->setTtl(10);
102         $this->assertTrue($response->isCacheable());
103     }
104
105     public function testMustRevalidate()
106     {
107         $response = new Response();
108         $this->assertFalse($response->mustRevalidate());
109     }
110
111     public function testMustRevalidateWithMustRevalidateCacheControlHeader()
112     {
113         $response = new Response();
114         $response->headers->set('cache-control', 'must-revalidate');
115
116         $this->assertTrue($response->mustRevalidate());
117     }
118
119     public function testMustRevalidateWithProxyRevalidateCacheControlHeader()
120     {
121         $response = new Response();
122         $response->headers->set('cache-control', 'proxy-revalidate');
123
124         $this->assertTrue($response->mustRevalidate());
125     }
126
127     public function testSetNotModified()
128     {
129         $response = new Response('foo');
130         $modified = $response->setNotModified();
131         $this->assertObjectHasAttribute('headers', $modified);
132         $this->assertObjectHasAttribute('content', $modified);
133         $this->assertObjectHasAttribute('version', $modified);
134         $this->assertObjectHasAttribute('statusCode', $modified);
135         $this->assertObjectHasAttribute('statusText', $modified);
136         $this->assertObjectHasAttribute('charset', $modified);
137         $this->assertEquals(304, $modified->getStatusCode());
138
139         ob_start();
140         $modified->sendContent();
141         $string = ob_get_clean();
142         $this->assertEmpty($string);
143     }
144
145     public function testIsSuccessful()
146     {
147         $response = new Response();
148         $this->assertTrue($response->isSuccessful());
149     }
150
151     public function testIsNotModified()
152     {
153         $response = new Response();
154         $modified = $response->isNotModified(new Request());
155         $this->assertFalse($modified);
156     }
157
158     public function testIsNotModifiedNotSafe()
159     {
160         $request = Request::create('/homepage', 'POST');
161
162         $response = new Response();
163         $this->assertFalse($response->isNotModified($request));
164     }
165
166     public function testIsNotModifiedLastModified()
167     {
168         $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
169         $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
170         $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
171
172         $request = new Request();
173         $request->headers->set('If-Modified-Since', $modified);
174
175         $response = new Response();
176
177         $response->headers->set('Last-Modified', $modified);
178         $this->assertTrue($response->isNotModified($request));
179
180         $response->headers->set('Last-Modified', $before);
181         $this->assertTrue($response->isNotModified($request));
182
183         $response->headers->set('Last-Modified', $after);
184         $this->assertFalse($response->isNotModified($request));
185
186         $response->headers->set('Last-Modified', '');
187         $this->assertFalse($response->isNotModified($request));
188     }
189
190     public function testIsNotModifiedEtag()
191     {
192         $etagOne = 'randomly_generated_etag';
193         $etagTwo = 'randomly_generated_etag_2';
194
195         $request = new Request();
196         $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree'));
197
198         $response = new Response();
199
200         $response->headers->set('ETag', $etagOne);
201         $this->assertTrue($response->isNotModified($request));
202
203         $response->headers->set('ETag', $etagTwo);
204         $this->assertTrue($response->isNotModified($request));
205
206         $response->headers->set('ETag', '');
207         $this->assertFalse($response->isNotModified($request));
208     }
209
210     public function testIsNotModifiedLastModifiedAndEtag()
211     {
212         $before = 'Sun, 25 Aug 2013 18:32:31 GMT';
213         $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
214         $after = 'Sun, 25 Aug 2013 19:33:31 GMT';
215         $etag = 'randomly_generated_etag';
216
217         $request = new Request();
218         $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
219         $request->headers->set('If-Modified-Since', $modified);
220
221         $response = new Response();
222
223         $response->headers->set('ETag', $etag);
224         $response->headers->set('Last-Modified', $after);
225         $this->assertFalse($response->isNotModified($request));
226
227         $response->headers->set('ETag', 'non-existent-etag');
228         $response->headers->set('Last-Modified', $before);
229         $this->assertFalse($response->isNotModified($request));
230
231         $response->headers->set('ETag', $etag);
232         $response->headers->set('Last-Modified', $modified);
233         $this->assertTrue($response->isNotModified($request));
234     }
235
236     public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified()
237     {
238         $modified = 'Sun, 25 Aug 2013 18:33:31 GMT';
239         $etag = 'randomly_generated_etag';
240
241         $request = new Request();
242         $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree'));
243         $request->headers->set('If-Modified-Since', $modified);
244
245         $response = new Response();
246
247         $response->headers->set('ETag', $etag);
248         $this->assertTrue($response->isNotModified($request));
249
250         $response->headers->set('ETag', 'non-existent-etag');
251         $this->assertFalse($response->isNotModified($request));
252     }
253
254     public function testIsValidateable()
255     {
256         $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
257         $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present');
258
259         $response = new Response('', 200, array('ETag' => '"12345"'));
260         $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present');
261
262         $response = new Response();
263         $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present');
264     }
265
266     public function testGetDate()
267     {
268         $oneHourAgo = $this->createDateTimeOneHourAgo();
269         $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822)));
270         $date = $response->getDate();
271         $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present');
272
273         $response = new Response();
274         $date = $response->getDate();
275         $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present');
276
277         $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)));
278         $now = $this->createDateTimeNow();
279         $response->headers->set('Date', $now->format(DATE_RFC2822));
280         $date = $response->getDate();
281         $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified');
282
283         $response = new Response('', 200);
284         $now = $this->createDateTimeNow();
285         $response->headers->remove('Date');
286         $date = $response->getDate();
287         $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed');
288     }
289
290     public function testGetMaxAge()
291     {
292         $response = new Response();
293         $response->headers->set('Cache-Control', 's-maxage=600, max-age=0');
294         $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present');
295
296         $response = new Response();
297         $response->headers->set('Cache-Control', 'max-age=600');
298         $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present');
299
300         $response = new Response();
301         $response->headers->set('Cache-Control', 'must-revalidate');
302         $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
303         $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present');
304
305         $response = new Response();
306         $response->headers->set('Cache-Control', 'must-revalidate');
307         $response->headers->set('Expires', -1);
308         $this->assertEquals('Sat, 01 Jan 00 00:00:00 +0000', $response->getExpires()->format(DATE_RFC822));
309
310         $response = new Response();
311         $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available');
312     }
313
314     public function testSetSharedMaxAge()
315     {
316         $response = new Response();
317         $response->setSharedMaxAge(20);
318
319         $cacheControl = $response->headers->get('Cache-Control');
320         $this->assertEquals('public, s-maxage=20', $cacheControl);
321     }
322
323     public function testIsPrivate()
324     {
325         $response = new Response();
326         $response->headers->set('Cache-Control', 'max-age=100');
327         $response->setPrivate();
328         $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
329         $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
330
331         $response = new Response();
332         $response->headers->set('Cache-Control', 'public, max-age=100');
333         $response->setPrivate();
334         $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true');
335         $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true');
336         $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive');
337     }
338
339     public function testExpire()
340     {
341         $response = new Response();
342         $response->headers->set('Cache-Control', 'max-age=100');
343         $response->expire();
344         $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present');
345
346         $response = new Response();
347         $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500');
348         $response->expire();
349         $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present');
350
351         $response = new Response();
352         $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500');
353         $response->headers->set('Age', '1000');
354         $response->expire();
355         $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired');
356
357         $response = new Response();
358         $response->expire();
359         $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information');
360
361         $response = new Response();
362         $response->headers->set('Expires', -1);
363         $response->expire();
364         $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired');
365
366         $response = new Response();
367         $response->headers->set('Expires', date(DATE_RFC2822, time() + 600));
368         $response->expire();
369         $this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh');
370     }
371
372     public function testGetTtl()
373     {
374         $response = new Response();
375         $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present');
376
377         $response = new Response();
378         $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822));
379         $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present');
380
381         $response = new Response();
382         $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822));
383         $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past');
384
385         $response = new Response();
386         $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822));
387         $response->headers->set('Age', 0);
388         $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero');
389
390         $response = new Response();
391         $response->headers->set('Cache-Control', 'max-age=60');
392         $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present');
393     }
394
395     public function testSetClientTtl()
396     {
397         $response = new Response();
398         $response->setClientTtl(10);
399
400         $this->assertEquals($response->getMaxAge(), $response->getAge() + 10);
401     }
402
403     public function testGetSetProtocolVersion()
404     {
405         $response = new Response();
406
407         $this->assertEquals('1.0', $response->getProtocolVersion());
408
409         $response->setProtocolVersion('1.1');
410
411         $this->assertEquals('1.1', $response->getProtocolVersion());
412     }
413
414     public function testGetVary()
415     {
416         $response = new Response();
417         $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present');
418
419         $response = new Response();
420         $response->headers->set('Vary', 'Accept-Language');
421         $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value');
422
423         $response = new Response();
424         $response->headers->set('Vary', 'Accept-Language User-Agent    X-Foo');
425         $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces');
426
427         $response = new Response();
428         $response->headers->set('Vary', 'Accept-Language,User-Agent,    X-Foo');
429         $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas');
430
431         $vary = array('Accept-Language', 'User-Agent', 'X-foo');
432
433         $response = new Response();
434         $response->headers->set('Vary', $vary);
435         $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
436
437         $response = new Response();
438         $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo');
439         $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays');
440     }
441
442     public function testSetVary()
443     {
444         $response = new Response();
445         $response->setVary('Accept-Language');
446         $this->assertEquals(array('Accept-Language'), $response->getVary());
447
448         $response->setVary('Accept-Language, User-Agent');
449         $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default');
450
451         $response->setVary('X-Foo', false);
452         $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false');
453     }
454
455     public function testDefaultContentType()
456     {
457         $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock();
458         $headerMock->expects($this->at(0))
459             ->method('set')
460             ->with('Content-Type', 'text/html');
461         $headerMock->expects($this->at(1))
462             ->method('set')
463             ->with('Content-Type', 'text/html; charset=UTF-8');
464
465         $response = new Response('foo');
466         $response->headers = $headerMock;
467
468         $response->prepare(new Request());
469     }
470
471     public function testContentTypeCharset()
472     {
473         $response = new Response();
474         $response->headers->set('Content-Type', 'text/css');
475
476         // force fixContentType() to be called
477         $response->prepare(new Request());
478
479         $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type'));
480     }
481
482     public function testPrepareDoesNothingIfContentTypeIsSet()
483     {
484         $response = new Response('foo');
485         $response->headers->set('Content-Type', 'text/plain');
486
487         $response->prepare(new Request());
488
489         $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type'));
490     }
491
492     public function testPrepareDoesNothingIfRequestFormatIsNotDefined()
493     {
494         $response = new Response('foo');
495
496         $response->prepare(new Request());
497
498         $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type'));
499     }
500
501     public function testPrepareSetContentType()
502     {
503         $response = new Response('foo');
504         $request = Request::create('/');
505         $request->setRequestFormat('json');
506
507         $response->prepare($request);
508
509         $this->assertEquals('application/json', $response->headers->get('content-type'));
510     }
511
512     public function testPrepareRemovesContentForHeadRequests()
513     {
514         $response = new Response('foo');
515         $request = Request::create('/', 'HEAD');
516
517         $length = 12345;
518         $response->headers->set('Content-Length', $length);
519         $response->prepare($request);
520
521         $this->assertEquals('', $response->getContent());
522         $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13');
523     }
524
525     public function testPrepareRemovesContentForInformationalResponse()
526     {
527         $response = new Response('foo');
528         $request = Request::create('/');
529
530         $response->setContent('content');
531         $response->setStatusCode(101);
532         $response->prepare($request);
533         $this->assertEquals('', $response->getContent());
534         $this->assertFalse($response->headers->has('Content-Type'));
535         $this->assertFalse($response->headers->has('Content-Type'));
536
537         $response->setContent('content');
538         $response->setStatusCode(304);
539         $response->prepare($request);
540         $this->assertEquals('', $response->getContent());
541         $this->assertFalse($response->headers->has('Content-Type'));
542         $this->assertFalse($response->headers->has('Content-Length'));
543     }
544
545     public function testPrepareRemovesContentLength()
546     {
547         $response = new Response('foo');
548         $request = Request::create('/');
549
550         $response->headers->set('Content-Length', 12345);
551         $response->prepare($request);
552         $this->assertEquals(12345, $response->headers->get('Content-Length'));
553
554         $response->headers->set('Transfer-Encoding', 'chunked');
555         $response->prepare($request);
556         $this->assertFalse($response->headers->has('Content-Length'));
557     }
558
559     public function testPrepareSetsPragmaOnHttp10Only()
560     {
561         $request = Request::create('/', 'GET');
562         $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0');
563
564         $response = new Response('foo');
565         $response->prepare($request);
566         $this->assertEquals('no-cache', $response->headers->get('pragma'));
567         $this->assertEquals('-1', $response->headers->get('expires'));
568
569         $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1');
570         $response = new Response('foo');
571         $response->prepare($request);
572         $this->assertFalse($response->headers->has('pragma'));
573         $this->assertFalse($response->headers->has('expires'));
574     }
575
576     public function testSetCache()
577     {
578         $response = new Response();
579         //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public')
580         try {
581             $response->setCache(array('wrong option' => 'value'));
582             $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported');
583         } catch (\Exception $e) {
584             $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported');
585             $this->assertContains('"wrong option"', $e->getMessage());
586         }
587
588         $options = array('etag' => '"whatever"');
589         $response->setCache($options);
590         $this->assertEquals($response->getEtag(), '"whatever"');
591
592         $now = $this->createDateTimeNow();
593         $options = array('last_modified' => $now);
594         $response->setCache($options);
595         $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp());
596
597         $options = array('max_age' => 100);
598         $response->setCache($options);
599         $this->assertEquals($response->getMaxAge(), 100);
600
601         $options = array('s_maxage' => 200);
602         $response->setCache($options);
603         $this->assertEquals($response->getMaxAge(), 200);
604
605         $this->assertTrue($response->headers->hasCacheControlDirective('public'));
606         $this->assertFalse($response->headers->hasCacheControlDirective('private'));
607
608         $response->setCache(array('public' => true));
609         $this->assertTrue($response->headers->hasCacheControlDirective('public'));
610         $this->assertFalse($response->headers->hasCacheControlDirective('private'));
611
612         $response->setCache(array('public' => false));
613         $this->assertFalse($response->headers->hasCacheControlDirective('public'));
614         $this->assertTrue($response->headers->hasCacheControlDirective('private'));
615
616         $response->setCache(array('private' => true));
617         $this->assertFalse($response->headers->hasCacheControlDirective('public'));
618         $this->assertTrue($response->headers->hasCacheControlDirective('private'));
619
620         $response->setCache(array('private' => false));
621         $this->assertTrue($response->headers->hasCacheControlDirective('public'));
622         $this->assertFalse($response->headers->hasCacheControlDirective('private'));
623
624         $response->setCache(array('immutable' => true));
625         $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
626
627         $response->setCache(array('immutable' => false));
628         $this->assertFalse($response->headers->hasCacheControlDirective('immutable'));
629     }
630
631     public function testSendContent()
632     {
633         $response = new Response('test response rendering', 200);
634
635         ob_start();
636         $response->sendContent();
637         $string = ob_get_clean();
638         $this->assertContains('test response rendering', $string);
639     }
640
641     public function testSetPublic()
642     {
643         $response = new Response();
644         $response->setPublic();
645
646         $this->assertTrue($response->headers->hasCacheControlDirective('public'));
647         $this->assertFalse($response->headers->hasCacheControlDirective('private'));
648     }
649
650     public function testSetImmutable()
651     {
652         $response = new Response();
653         $response->setImmutable();
654
655         $this->assertTrue($response->headers->hasCacheControlDirective('immutable'));
656     }
657
658     public function testIsImmutable()
659     {
660         $response = new Response();
661         $response->setImmutable();
662
663         $this->assertTrue($response->isImmutable());
664     }
665
666     public function testSetExpires()
667     {
668         $response = new Response();
669         $response->setExpires(null);
670
671         $this->assertNull($response->getExpires(), '->setExpires() remove the header when passed null');
672
673         $now = $this->createDateTimeNow();
674         $response->setExpires($now);
675
676         $this->assertEquals($response->getExpires()->getTimestamp(), $now->getTimestamp());
677     }
678
679     public function testSetLastModified()
680     {
681         $response = new Response();
682         $response->setLastModified($this->createDateTimeNow());
683         $this->assertNotNull($response->getLastModified());
684
685         $response->setLastModified(null);
686         $this->assertNull($response->getLastModified());
687     }
688
689     public function testIsInvalid()
690     {
691         $response = new Response();
692
693         try {
694             $response->setStatusCode(99);
695             $this->fail();
696         } catch (\InvalidArgumentException $e) {
697             $this->assertTrue($response->isInvalid());
698         }
699
700         try {
701             $response->setStatusCode(650);
702             $this->fail();
703         } catch (\InvalidArgumentException $e) {
704             $this->assertTrue($response->isInvalid());
705         }
706
707         $response = new Response('', 200);
708         $this->assertFalse($response->isInvalid());
709     }
710
711     /**
712      * @dataProvider getStatusCodeFixtures
713      */
714     public function testSetStatusCode($code, $text, $expectedText)
715     {
716         $response = new Response();
717
718         $response->setStatusCode($code, $text);
719
720         $statusText = new \ReflectionProperty($response, 'statusText');
721         $statusText->setAccessible(true);
722
723         $this->assertEquals($expectedText, $statusText->getValue($response));
724     }
725
726     public function getStatusCodeFixtures()
727     {
728         return array(
729             array('200', null, 'OK'),
730             array('200', false, ''),
731             array('200', 'foo', 'foo'),
732             array('199', null, 'unknown status'),
733             array('199', false, ''),
734             array('199', 'foo', 'foo'),
735         );
736     }
737
738     public function testIsInformational()
739     {
740         $response = new Response('', 100);
741         $this->assertTrue($response->isInformational());
742
743         $response = new Response('', 200);
744         $this->assertFalse($response->isInformational());
745     }
746
747     public function testIsRedirectRedirection()
748     {
749         foreach (array(301, 302, 303, 307) as $code) {
750             $response = new Response('', $code);
751             $this->assertTrue($response->isRedirection());
752             $this->assertTrue($response->isRedirect());
753         }
754
755         $response = new Response('', 304);
756         $this->assertTrue($response->isRedirection());
757         $this->assertFalse($response->isRedirect());
758
759         $response = new Response('', 200);
760         $this->assertFalse($response->isRedirection());
761         $this->assertFalse($response->isRedirect());
762
763         $response = new Response('', 404);
764         $this->assertFalse($response->isRedirection());
765         $this->assertFalse($response->isRedirect());
766
767         $response = new Response('', 301, array('Location' => '/good-uri'));
768         $this->assertFalse($response->isRedirect('/bad-uri'));
769         $this->assertTrue($response->isRedirect('/good-uri'));
770     }
771
772     public function testIsNotFound()
773     {
774         $response = new Response('', 404);
775         $this->assertTrue($response->isNotFound());
776
777         $response = new Response('', 200);
778         $this->assertFalse($response->isNotFound());
779     }
780
781     public function testIsEmpty()
782     {
783         foreach (array(204, 304) as $code) {
784             $response = new Response('', $code);
785             $this->assertTrue($response->isEmpty());
786         }
787
788         $response = new Response('', 200);
789         $this->assertFalse($response->isEmpty());
790     }
791
792     public function testIsForbidden()
793     {
794         $response = new Response('', 403);
795         $this->assertTrue($response->isForbidden());
796
797         $response = new Response('', 200);
798         $this->assertFalse($response->isForbidden());
799     }
800
801     public function testIsOk()
802     {
803         $response = new Response('', 200);
804         $this->assertTrue($response->isOk());
805
806         $response = new Response('', 404);
807         $this->assertFalse($response->isOk());
808     }
809
810     public function testIsServerOrClientError()
811     {
812         $response = new Response('', 404);
813         $this->assertTrue($response->isClientError());
814         $this->assertFalse($response->isServerError());
815
816         $response = new Response('', 500);
817         $this->assertFalse($response->isClientError());
818         $this->assertTrue($response->isServerError());
819     }
820
821     public function testHasVary()
822     {
823         $response = new Response();
824         $this->assertFalse($response->hasVary());
825
826         $response->setVary('User-Agent');
827         $this->assertTrue($response->hasVary());
828     }
829
830     public function testSetEtag()
831     {
832         $response = new Response('', 200, array('ETag' => '"12345"'));
833         $response->setEtag();
834
835         $this->assertNull($response->headers->get('Etag'), '->setEtag() removes Etags when call with null');
836     }
837
838     /**
839      * @dataProvider validContentProvider
840      */
841     public function testSetContent($content)
842     {
843         $response = new Response();
844         $response->setContent($content);
845         $this->assertEquals((string) $content, $response->getContent());
846     }
847
848     /**
849      * @expectedException \UnexpectedValueException
850      * @dataProvider invalidContentProvider
851      */
852     public function testSetContentInvalid($content)
853     {
854         $response = new Response();
855         $response->setContent($content);
856     }
857
858     public function testSettersAreChainable()
859     {
860         $response = new Response();
861
862         $setters = array(
863             'setProtocolVersion' => '1.0',
864             'setCharset' => 'UTF-8',
865             'setPublic' => null,
866             'setPrivate' => null,
867             'setDate' => $this->createDateTimeNow(),
868             'expire' => null,
869             'setMaxAge' => 1,
870             'setSharedMaxAge' => 1,
871             'setTtl' => 1,
872             'setClientTtl' => 1,
873         );
874
875         foreach ($setters as $setter => $arg) {
876             $this->assertEquals($response, $response->{$setter}($arg));
877         }
878     }
879
880     public function testNoDeprecationsAreTriggered()
881     {
882         new DefaultResponse();
883         $this->getMockBuilder(Response::class)->getMock();
884
885         // we just need to ensure that subclasses of Response can be created without any deprecations
886         // being triggered if the subclass does not override any final methods
887         $this->addToAssertionCount(1);
888     }
889
890     public function validContentProvider()
891     {
892         return array(
893             'obj' => array(new StringableObject()),
894             'string' => array('Foo'),
895             'int' => array(2),
896         );
897     }
898
899     public function invalidContentProvider()
900     {
901         return array(
902             'obj' => array(new \stdClass()),
903             'array' => array(array()),
904             'bool' => array(true, '1'),
905         );
906     }
907
908     protected function createDateTimeOneHourAgo()
909     {
910         return $this->createDateTimeNow()->sub(new \DateInterval('PT1H'));
911     }
912
913     protected function createDateTimeOneHourLater()
914     {
915         return $this->createDateTimeNow()->add(new \DateInterval('PT1H'));
916     }
917
918     protected function createDateTimeNow()
919     {
920         $date = new \DateTime();
921
922         return $date->setTimestamp(time());
923     }
924
925     protected function provideResponse()
926     {
927         return new Response();
928     }
929
930     /**
931      * @see       http://github.com/zendframework/zend-diactoros for the canonical source repository
932      *
933      * @author    Fábio Pacheco
934      * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
935      * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
936      */
937     public function ianaCodesReasonPhrasesProvider()
938     {
939         if (!\in_array('https', stream_get_wrappers(), true)) {
940             $this->markTestSkipped('The "https" wrapper is not available');
941         }
942
943         $ianaHttpStatusCodes = new \DOMDocument();
944
945         libxml_set_streams_context(stream_context_create(array(
946             'http' => array(
947                 'method' => 'GET',
948                 'timeout' => 30,
949             ),
950         )));
951
952         $ianaHttpStatusCodes->load('https://www.iana.org/assignments/http-status-codes/http-status-codes.xml');
953         if (!$ianaHttpStatusCodes->relaxNGValidate(__DIR__.'/schema/http-status-codes.rng')) {
954             self::fail('Invalid IANA\'s HTTP status code list.');
955         }
956
957         $ianaCodesReasonPhrases = array();
958
959         $xpath = new \DOMXPath($ianaHttpStatusCodes);
960         $xpath->registerNamespace('ns', 'http://www.iana.org/assignments');
961
962         $records = $xpath->query('//ns:record');
963         foreach ($records as $record) {
964             $value = $xpath->query('.//ns:value', $record)->item(0)->nodeValue;
965             $description = $xpath->query('.//ns:description', $record)->item(0)->nodeValue;
966
967             if (\in_array($description, array('Unassigned', '(Unused)'), true)) {
968                 continue;
969             }
970
971             if (preg_match('/^([0-9]+)\s*\-\s*([0-9]+)$/', $value, $matches)) {
972                 for ($value = $matches[1]; $value <= $matches[2]; ++$value) {
973                     $ianaCodesReasonPhrases[] = array($value, $description);
974                 }
975             } else {
976                 $ianaCodesReasonPhrases[] = array($value, $description);
977             }
978         }
979
980         return $ianaCodesReasonPhrases;
981     }
982
983     /**
984      * @dataProvider ianaCodesReasonPhrasesProvider
985      */
986     public function testReasonPhraseDefaultsAgainstIana($code, $reasonPhrase)
987     {
988         $this->assertEquals($reasonPhrase, Response::$statusTexts[$code]);
989     }
990 }
991
992 class StringableObject
993 {
994     public function __toString()
995     {
996         return 'Foo';
997     }
998 }
999
1000 class DefaultResponse extends Response
1001 {
1002 }
1003
1004 class ExtendedResponse extends Response
1005 {
1006     public function setLastModified(\DateTime $date = null)
1007     {
1008     }
1009
1010     public function getDate()
1011     {
1012     }
1013 }