atorTest.php$0   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 9
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
wmc 1
lcom 0
cbo 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Tests\Knp\Snappy;
4
5
use Knp\Snappy\AbstractGenerator;
6
use PHPUnit\Framework\TestCase;
7
use Psr\Log\LoggerInterface;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use ReflectionProperty;
11
use ReflectionMethod;
12
13
class AbstractGeneratorTest extends TestCase
14
{
15
    public function testAddOption(): void
16
    {
17
        $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false);
18
19
        $this->assertEquals([], $media->getOptions());
20
21
        $r = new ReflectionMethod($media, 'addOption');
22
        $r->setAccessible(true);
23
        $r->invokeArgs($media, ['foo', 'bar']);
24
25
        $this->assertEquals(['foo' => 'bar'], $media->getOptions(), '->addOption() adds an option');
26
27
        $r->invokeArgs($media, ['baz', 'bat']);
28
29
        $this->assertEquals(
30
            [
31
                'foo' => 'bar',
32
                'baz' => 'bat',
33
            ],
34
            $media->getOptions(),
35
            '->addOption() appends the option to the existing ones'
36
        );
37
38
        $message = '->addOption() raises an exception when the specified option already exists';
39
40
        try {
41
            $r->invokeArgs($media, ['baz', 'bat']);
42
            $this->fail($message);
43
        } catch (InvalidArgumentException $e) {
44
            $this->anything();
45
        }
46
    }
47
48
    public function testAddOptions(): void
49
    {
50
        $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false);
51
52
        $this->assertEquals([], $media->getOptions());
53
54
        $r = new ReflectionMethod($media, 'addOptions');
55
        $r->setAccessible(true);
56
        $r->invokeArgs($media, [['foo' => 'bar', 'baz' => 'bat']]);
57
58
        $this->assertEquals(
59
            [
60
                'foo' => 'bar',
61
                'baz' => 'bat',
62
            ],
63
            $media->getOptions(),
64
            '->addOptions() adds all the given options'
65
        );
66
67
        $r->invokeArgs($media, [['ban' => 'bag', 'bal' => 'bac']]);
68
69
        $this->assertEquals(
70
            [
71
                'foo' => 'bar',
72
                'baz' => 'bat',
73
                'ban' => 'bag',
74
                'bal' => 'bac',
75
            ],
76
            $media->getOptions(),
77
            '->addOptions() adds the given options to the existing ones'
78
        );
79
80
        $message = '->addOptions() raises an exception when one of the given options already exists';
81
82
        try {
83
            $r->invokeArgs($media, [['bak' => 'bam', 'bah' => 'bap', 'baz' => 'bat']]);
84
            $this->fail($message);
85
        } catch (InvalidArgumentException $e) {
86
            $this->anything();
87
        }
88
    }
89
90
    public function testSetOption(): void
91
    {
92
        $media = $this
93
            ->getMockBuilder(AbstractGenerator::class)
94
            ->setConstructorArgs(['/usr/local/bin/wkhtmltopdf'])
95
            ->getMockForAbstractClass()
96
        ;
97
98
        $logger = $this
99
            ->getMockBuilder(LoggerInterface::class)
100
            ->getMock()
101
        ;
102
        $media->setLogger($logger);
103
        $logger->expects($this->once())->method('debug');
104
105
        $r = new ReflectionMethod($media, 'addOption');
106
        $r->setAccessible(true);
107
        $r->invokeArgs($media, ['foo', 'bar']);
108
109
        $media->setOption('foo', 'abc');
110
111
        $this->assertEquals(
112
            [
113
                'foo' => 'abc',
114
            ],
115
            $media->getOptions(),
116
            '->setOption() defines the value of an option'
117
        );
118
119
        $message = '->setOption() raises an exception when the specified option does not exist';
120
121
        try {
122
            $media->setOption('bad', 'def');
123
            $this->fail($message);
124
        } catch (InvalidArgumentException $e) {
125
            $this->anything();
126
        }
127
    }
128
129
    public function testSetOptions(): void
130
    {
131
        $media = $this
132
            ->getMockBuilder(AbstractGenerator::class)
133
            ->setConstructorArgs(['/usr/local/bin/wkhtmltopdf'])
134
            ->getMockForAbstractClass()
135
        ;
136
137
        $logger = $this
138
            ->getMockBuilder(LoggerInterface::class)
139
            ->getMock()
140
        ;
141
        $media->setLogger($logger);
142
        $logger->expects($this->exactly(4))->method('debug');
143
144
        $r = new ReflectionMethod($media, 'addOptions');
145
        $r->setAccessible(true);
146
        $r->invokeArgs($media, [['foo' => 'bar', 'baz' => 'bat']]);
147
148
        $media->setOptions(['foo' => 'abc', 'baz' => 'def']);
149
150
        $this->assertEquals(
151
            [
152
                'foo' => 'abc',
153
                'baz' => 'def',
154
            ],
155
            $media->getOptions(),
156
            '->setOptions() defines the values of all the specified options'
157
        );
158
159
        $message = '->setOptions() raises an exception when one of the specified options does not exist';
160
161
        try {
162
            $media->setOptions(['foo' => 'abc', 'baz' => 'def', 'bad' => 'ghi']);
163
            $this->fail($message);
164
        } catch (InvalidArgumentException $e) {
165
            $this->anything();
166
        }
167
    }
168
169
    public function testGenerate(): void
170
    {
171
        $media = $this->getMockBuilder(AbstractGenerator::class)
172
            ->setMethods([
173
                'configure',
174
                'prepareOutput',
175
                'getCommand',
176
                'executeCommand',
177
                'checkOutput',
178
                'checkProcessStatus',
179
            ])
180
            ->setConstructorArgs(['the_binary', []])
181
            ->getMock()
182
        ;
183
184
        $logger = $this
185
            ->getMockBuilder(LoggerInterface::class)
186
            ->getMock()
187
        ;
188
        $media->setLogger($logger);
189
        $logger
190
            ->expects($this->exactly(2))
191
            ->method('info')
192
            ->with(
193
                $this->logicalOr(
194
                    'Generate from file(s) "the_input_file" to file "the_output_file".',
195
                    'File "the_output_file" has been successfully generated.'
196
                ),
197
                $this->logicalOr(
198
                    ['command' => 'the command', 'env' => null, 'timeout' => false],
199
                    ['command' => 'the command', 'stdout' => 'stdout', 'stderr' => 'stderr']
200
                )
201
            )
202
        ;
203
204
        $media
205
            ->expects($this->once())
206
            ->method('prepareOutput')
207
            ->with($this->equalTo('the_output_file'))
208
        ;
209
        $media
210
            ->expects($this->any())
211
            ->method('getCommand')
212
            ->with(
213
                $this->equalTo('the_input_file'),
214
                $this->equalTo('the_output_file'),
215
                $this->equalTo(['foo' => 'bar'])
216
            )
217
            ->will($this->returnValue('the command'))
218
        ;
219
        $media
220
            ->expects($this->once())
221
            ->method('executeCommand')
222
            ->with($this->equalTo('the command'))
223
            ->willReturn([0, 'stdout', 'stderr'])
224
        ;
225
        $media
226
            ->expects($this->once())
227
            ->method('checkProcessStatus')
228
            ->with(0, 'stdout', 'stderr', 'the command')
229
        ;
230
        $media
231
            ->expects($this->once())
232
            ->method('checkOutput')
233
            ->with(
234
                $this->equalTo('the_output_file'),
235
                $this->equalTo('the command')
236
            )
237
        ;
238
239
        $media->generate('the_input_file', 'the_output_file', ['foo' => 'bar']);
240
    }
241
242
    public function testFailingGenerate(): void
243
    {
244
        $media = $this->getMockBuilder(AbstractGenerator::class)
245
            ->setMethods([
246
                'configure',
247
                'prepareOutput',
248
                'getCommand',
249
                'executeCommand',
250
                'checkOutput',
251
                'checkProcessStatus',
252
            ])
253
            ->setConstructorArgs(['the_binary', [], ['PATH' => '/usr/bin']])
254
            ->getMock()
255
        ;
256
257
        $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
258
        $media->setLogger($logger);
259
        $media->setTimeout(2000);
260
261
        $logger
262
            ->expects($this->once())
263
            ->method('info')
264
            ->with(
265
                $this->equalTo('Generate from file(s) "the_input_file" to file "the_output_file".'),
266
                $this->equalTo(['command' => 'the command', 'env' => ['PATH' => '/usr/bin'], 'timeout' => 2000])
267
            )
268
        ;
269
270
        $logger
271
            ->expects($this->once())
272
            ->method('error')
273
            ->with(
274
                $this->equalTo('An error happened while generating "the_output_file".'),
275
                $this->equalTo(['command' => 'the command', 'status' => 1, 'stdout' => 'stdout', 'stderr' => 'stderr'])
276
            )
277
        ;
278
279
        $media
280
            ->expects($this->once())
281
            ->method('prepareOutput')
282
            ->with($this->equalTo('the_output_file'))
283
        ;
284
        $media
285
            ->expects($this->any())
286
            ->method('getCommand')
287
            ->with(
288
                $this->equalTo('the_input_file'),
289
                $this->equalTo('the_output_file')
290
            )
291
            ->will($this->returnValue('the command'))
292
        ;
293
        $media
294
            ->expects($this->once())
295
            ->method('executeCommand')
296
            ->with($this->equalTo('the command'))
297
            ->willReturn([1, 'stdout', 'stderr'])
298
        ;
299
        $media
300
            ->expects($this->once())
301
            ->method('checkProcessStatus')
302
            ->with(1, 'stdout', 'stderr', 'the command')
303
            ->willThrowException(new RuntimeException())
304
        ;
305
306
        $this->expectException(RuntimeException::class);
307
308
        $media->generate('the_input_file', 'the_output_file', ['foo' => 'bar']);
309
    }
310
311
    public function testGenerateFromHtml(): void
312
    {
313
        $media = $this->getMockBuilder(AbstractGenerator::class)
314
            ->setMethods([
315
                'configure',
316
                'generate',
317
                'createTemporaryFile',
318
            ])
319
            ->setConstructorArgs(['the_binary'])
320
            ->disableOriginalConstructor()
321
            ->getMock()
322
        ;
323
324
        $media
325
            ->expects($this->once())
326
            ->method('createTemporaryFile')
327
            ->with(
328
                $this->equalTo('<html>foo</html>'),
329
                $this->equalTo('html')
330
            )
331
            ->will($this->returnValue('the_temporary_file'))
332
        ;
333
        $media
334
            ->expects($this->once())
335
            ->method('generate')
336
            ->with(
337
                $this->equalTo(['the_temporary_file']),
338
                $this->equalTo('the_output_file'),
339
                $this->equalTo(['foo' => 'bar'])
340
            )
341
        ;
342
343
        $media->generateFromHtml('<html>foo</html>', 'the_output_file', ['foo' => 'bar']);
344
    }
345
346
    public function testGenerateFromHtmlWithHtmlArray(): void
347
    {
348
        $media = $this->getMockBuilder(AbstractGenerator::class)
349
            ->setMethods([
350
                'configure',
351
                'generate',
352
                'createTemporaryFile',
353
            ])
354
            ->setConstructorArgs(['the_binary'])
355
            ->disableOriginalConstructor()
356
            ->getMock()
357
        ;
358
        $media
359
            ->expects($this->once())
360
            ->method('createTemporaryFile')
361
            ->with(
362
                $this->equalTo('<html>foo</html>'),
363
                $this->equalTo('html')
364
            )
365
            ->will($this->returnValue('the_temporary_file'))
366
        ;
367
        $media
368
            ->expects($this->once())
369
            ->method('generate')
370
            ->with(
371
                $this->equalTo(['the_temporary_file']),
372
                $this->equalTo('the_output_file'),
373
                $this->equalTo(['foo' => 'bar'])
374
            )
375
        ;
376
377
        $media->generateFromHtml(['<html>foo</html>'], 'the_output_file', ['foo' => 'bar']);
378
    }
379
380
    public function testGetOutput(): void
381
    {
382
        $media = $this->getMockBuilder(AbstractGenerator::class)
383
            ->setMethods([
384
                'configure',
385
                'getDefaultExtension',
386
                'createTemporaryFile',
387
                'generate',
388
                'getFileContents',
389
                'unlink',
390
            ])
391
            ->disableOriginalConstructor()
392
            ->getMock()
393
        ;
394
        $media
395
            ->expects($this->any())
396
            ->method('getDefaultExtension')
397
            ->will($this->returnValue('ext'))
398
        ;
399
        $media
400
            ->expects($this->any())
401
            ->method('createTemporaryFile')
402
            ->with(
403
                $this->equalTo(null),
404
                $this->equalTo('ext')
405
            )
406
            ->will($this->returnValue('the_temporary_file'))
407
        ;
408
        $media
409
            ->expects($this->once())
410
            ->method('generate')
411
            ->with(
412
                $this->equalTo('the_input_file'),
413
                $this->equalTo('the_temporary_file'),
414
                $this->equalTo(['foo' => 'bar'])
415
            )
416
        ;
417
        $media
418
            ->expects($this->once())
419
            ->method('getFileContents')
420
            ->will($this->returnValue('the file contents'))
421
        ;
422
423
        $media
424
            ->expects($this->any())
425
            ->method('unlink')
426
            ->with($this->equalTo('the_temporary_file'))
427
            ->will($this->returnValue(true))
428
        ;
429
430
        $this->assertEquals('the file contents', $media->getOutput('the_input_file', ['foo' => 'bar']));
431
    }
432
433
    public function testGetOutputFromHtml(): void
434
    {
435
        $media = $this->getMockBuilder(AbstractGenerator::class)
436
            ->setMethods([
437
                'configure',
438
                'getOutput',
439
                'createTemporaryFile',
440
            ])
441
            ->disableOriginalConstructor()
442
            ->getMock()
443
        ;
444
        $media
445
            ->expects($this->once())
446
            ->method('createTemporaryFile')
447
            ->with(
448
                $this->equalTo('<html>foo</html>'),
449
                $this->equalTo('html')
450
            )
451
            ->will($this->returnValue('the_temporary_file'))
452
        ;
453
        $media
454
            ->expects($this->once())
455
            ->method('getOutput')
456
            ->with(
457
                $this->equalTo(['the_temporary_file']),
458
                $this->equalTo(['foo' => 'bar'])
459
            )
460
            ->will($this->returnValue('the output'))
461
        ;
462
463
        $this->assertEquals('the output', $media->getOutputFromHtml('<html>foo</html>', ['foo' => 'bar']));
464
    }
465
466
    public function testGetOutputFromHtmlWithHtmlArray(): void
467
    {
468
        $media = $this->getMockBuilder(AbstractGenerator::class)
469
            ->setMethods([
470
                'configure',
471
                'getOutput',
472
                'createTemporaryFile',
473
            ])
474
            ->disableOriginalConstructor()
475
            ->getMock()
476
        ;
477
        $media
478
            ->expects($this->once())
479
            ->method('createTemporaryFile')
480
            ->with(
481
                $this->equalTo('<html>foo</html>'),
482
                $this->equalTo('html')
483
            )
484
            ->will($this->returnValue('the_temporary_file'))
485
        ;
486
        $media
487
            ->expects($this->once())
488
            ->method('getOutput')
489
            ->with(
490
                $this->equalTo(['the_temporary_file']),
491
                $this->equalTo(['foo' => 'bar'])
492
            )
493
            ->will($this->returnValue('the output'))
494
        ;
495
496
        $this->assertEquals('the output', $media->getOutputFromHtml(['<html>foo</html>'], ['foo' => 'bar']));
497
    }
498
499
    public function testMergeOptions(): void
500
    {
501
        $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false);
502
503
        $originalOptions = ['foo' => 'bar', 'baz' => 'bat'];
504
505
        $addOptions = new ReflectionMethod($media, 'addOptions');
506
        $addOptions->setAccessible(true);
507
        $addOptions->invokeArgs($media, [$originalOptions]);
508
509
        $r = new ReflectionMethod($media, 'mergeOptions');
510
        $r->setAccessible(true);
511
512
        $mergedOptions = $r->invokeArgs($media, [['foo' => 'ban']]);
513
514
        $this->assertEquals(
515
            [
516
                'foo' => 'ban',
517
                'baz' => 'bat',
518
            ],
519
            $mergedOptions,
520
            '->mergeOptions() merges an option to the instance ones and returns the result options array'
521
        );
522
523
        $this->assertEquals(
524
            $originalOptions,
525
            $media->getOptions(),
526
            '->mergeOptions() does NOT change the instance options'
527
        );
528
529
        $mergedOptions = $r->invokeArgs($media, [['foo' => 'ban', 'baz' => 'bag']]);
530
531
        $this->assertEquals(
532
            [
533
                'foo' => 'ban',
534
                'baz' => 'bag',
535
            ],
536
            $mergedOptions,
537
            '->mergeOptions() merges many options to the instance ones and returns the result options array'
538
        );
539
540
        $message = '->mergeOptions() throws an InvalidArgumentException once there is an undefined option in the given array';
541
542
        try {
543
            $r->invokeArgs($media, [['foo' => 'ban', 'bad' => 'bah']]);
544
            $this->fail($message);
545
        } catch (InvalidArgumentException $e) {
546
            $this->anything();
547
        }
548
    }
549
550
    /**
551
     * @dataProvider dataForBuildCommand
552
     */
553
    public function testBuildCommand(string $binary, string $url, string $path, array $options, string $expected): void
554
    {
555
        $media = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false);
556
557
        $r = new ReflectionMethod($media, 'buildCommand');
558
        $r->setAccessible(true);
559
560
        $this->assertEquals($expected, $r->invokeArgs($media, [$binary, $url, $path, $options]));
561
    }
562
563
    public function dataForBuildCommand(): array
564
    {
565
        $theBinary = $this->getPHPExecutableFromPath() . ' -v'; // i.e.: '/usr/bin/php -v'
566
567
        return [
568
            [
569
                $theBinary,
570
                'http://the.url/',
571
                '/the/path',
572
                [],
573
                $theBinary . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
574
            ],
575
            [
576
                $theBinary,
577
                'http://the.url/',
578
                '/the/path',
579
                [
580
                    'foo' => null,
581
                    'bar' => false,
582
                    'baz' => [],
583
                ],
584
                $theBinary . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
585
            ],
586
            [
587
                $theBinary,
588
                'http://the.url/',
589
                '/the/path',
590
                [
591
                    'foo' => 'foovalue',
592
                    'bar' => ['barvalue1', 'barvalue2'],
593
                    'baz' => true,
594
                ],
595
                $theBinary . ' --foo ' . \escapeshellarg('foovalue') . ' --bar ' . \escapeshellarg('barvalue1') . ' --bar ' . \escapeshellarg('barvalue2') . ' --baz ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
596
            ],
597
            [
598
                $theBinary,
599
                'http://the.url/',
600
                '/the/path',
601
                [
602
                    'cookie' => ['session' => 'bla', 'phpsess' => 12],
603
                    'no-background' => '1',
604
                ],
605
                $theBinary . ' --cookie ' . \escapeshellarg('session') . ' ' . \escapeshellarg('bla') . ' --cookie ' . \escapeshellarg('phpsess') . ' ' . \escapeshellarg('12') . ' --no-background ' . \escapeshellarg('1') . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
606
            ],
607
            [
608
                $theBinary,
609
                'http://the.url/',
610
                '/the/path',
611
                [
612
                    'allow' => ['/path1', '/path2'],
613
                    'no-background' => '1',
614
                ],
615
                $theBinary . ' --allow ' . \escapeshellarg('/path1') . ' --allow ' . \escapeshellarg('/path2') . ' --no-background ' . \escapeshellarg('1') . ' ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
616
            ],
617
            [
618
                $theBinary,
619
                'http://the.url/',
620
                '/the/path',
621
                [
622
                    'image-dpi' => 100,
623
                    'image-quality' => 50,
624
                ],
625
                $theBinary . ' ' . '--image-dpi 100 --image-quality 50 ' . \escapeshellarg('http://the.url/') . ' ' . \escapeshellarg('/the/path'),
626
            ],
627
        ];
628
    }
629
630
    public function testCheckOutput(): void
631
    {
632
        $media = $this->getMockBuilder(AbstractGenerator::class)
633
            ->setMethods([
634
                'configure',
635
                'fileExists',
636
                'filesize',
637
            ])
638
            ->disableOriginalConstructor()
639
            ->getMock()
640
        ;
641
        $media
642
            ->expects($this->once())
643
            ->method('fileExists')
644
            ->with($this->equalTo('the_output_file'))
645
            ->will($this->returnValue(true))
646
        ;
647
        $media
648
            ->expects($this->once())
649
            ->method('filesize')
650
            ->with($this->equalTo('the_output_file'))
651
            ->will($this->returnValue(123))
652
        ;
653
654
        $r = new ReflectionMethod($media, 'checkOutput');
655
        $r->setAccessible(true);
656
657
        $message = '->checkOutput() checks both file existence and size';
658
659
        try {
660
            $r->invokeArgs($media, ['the_output_file', 'the command']);
661
            $this->anything();
662
        } catch (RuntimeException $e) {
663
            $this->fail($message);
664
        }
665
    }
666
667
    public function testCheckOutputWhenTheFileDoesNotExist(): void
668
    {
669
        $media = $this->getMockBuilder(AbstractGenerator::class)
670
            ->setMethods([
671
                'configure',
672
                'fileExists',
673
                'filesize',
674
            ])
675
            ->disableOriginalConstructor()
676
            ->getMock()
677
        ;
678
        $media
679
            ->expects($this->once())
680
            ->method('fileExists')
681
            ->with($this->equalTo('the_output_file'))
682
            ->will($this->returnValue(false))
683
        ;
684
685
        $r = new ReflectionMethod($media, 'checkOutput');
686
        $r->setAccessible(true);
687
688
        $message = '->checkOutput() throws an InvalidArgumentException when the file does not exist';
689
690
        try {
691
            $r->invokeArgs($media, ['the_output_file', 'the command']);
692
            $this->fail($message);
693
        } catch (RuntimeException $e) {
694
            $this->anything();
695
        }
696
    }
697
698
    public function testCheckOutputWhenTheFileIsEmpty(): void
699
    {
700
        $media = $this->getMockBuilder(AbstractGenerator::class)
701
            ->setMethods([
702
                'configure',
703
                'fileExists',
704
                'filesize',
705
            ])
706
            ->disableOriginalConstructor()
707
            ->getMock()
708
        ;
709
710
        $media
711
            ->expects($this->once())
712
            ->method('fileExists')
713
            ->with($this->equalTo('the_output_file'))
714
            ->will($this->returnValue(true))
715
        ;
716
        $media
717
            ->expects($this->once())
718
            ->method('filesize')
719
            ->with($this->equalTo('the_output_file'))
720
            ->will($this->returnValue(0))
721
        ;
722
723
        $r = new ReflectionMethod($media, 'checkOutput');
724
        $r->setAccessible(true);
725
726
        $message = '->checkOutput() throws an InvalidArgumentException when the file is empty';
727
728
        try {
729
            $r->invokeArgs($media, ['the_output_file', 'the command']);
730
            $this->fail($message);
731
        } catch (RuntimeException $e) {
732
            $this->anything();
733
        }
734
    }
735
736
    public function testCheckProcessStatus(): void
737
    {
738
        $media = $this->getMockBuilder(AbstractGenerator::class)
739
            ->setMethods(['configure'])
740
            ->disableOriginalConstructor()
741
            ->getMock()
742
        ;
743
744
        $r = new ReflectionMethod($media, 'checkProcessStatus');
745
        $r->setAccessible(true);
746
747
        try {
748
            $r->invokeArgs($media, [0, '', '', 'the command']);
749
            $this->anything();
750
        } catch (RuntimeException $e) {
751
            $this->fail('0 status means success');
752
        }
753
754
        try {
755
            $r->invokeArgs($media, [1, '', '', 'the command']);
756
            $this->anything();
757
        } catch (RuntimeException $e) {
758
            $this->fail('1 status means failure, but no stderr content');
759
        }
760
761
        try {
762
            $r->invokeArgs($media, [1, '', 'Could not connect to X', 'the command']);
763
            $this->fail('1 status means failure');
764
        } catch (RuntimeException $e) {
765
            $this->assertEquals(1, $e->getCode(), 'Exception thrown by checkProcessStatus should pass on the error code');
766
        }
767
    }
768
769
    /**
770
     * @dataProvider dataForIsAssociativeArray
771
     */
772
    public function testIsAssociativeArray(array $array, bool $isAssociativeArray): void
773
    {
774
        $generator = $this->getMockForAbstractClass(AbstractGenerator::class, [], '', false);
775
776
        $r = new ReflectionMethod($generator, 'isAssociativeArray');
777
        $r->setAccessible(true);
778
        $this->assertEquals($isAssociativeArray, $r->invokeArgs($generator, [$array]));
779
    }
780
781
    /**
782
     * @expectedException Knp\Snappy\Exception\FileAlreadyExistsException
783
     */
784
    public function testItThrowsTheProperExceptionWhenFileExistsAndNotOverwritting(): void
785
    {
786
        $media = $this->getMockBuilder(AbstractGenerator::class)
787
            ->setMethods([
788
                'configure',
789
                'fileExists',
790
                'isFile',
791
            ])
792
            ->disableOriginalConstructor()
793
            ->getMock()
794
        ;
795
796
        $media
797
            ->expects($this->any())
798
            ->method('fileExists')
799
            ->will($this->returnValue(true))
800
        ;
801
        $media
802
            ->expects($this->any())
803
            ->method('isFile')
804
            ->will($this->returnValue(true))
805
        ;
806
        $r = new ReflectionMethod($media, 'prepareOutput');
807
        $r->setAccessible(true);
808
809
        $r->invokeArgs($media, ['', false]);
810
    }
811
812
    public function dataForIsAssociativeArray(): array
813
    {
814
        return [
815
            [
816
                ['key' => 'value'],
817
                true,
818
            ],
819
            [
820
                ['key' => 2],
821
                true,
822
            ],
823
            [
824
                ['key' => 'value', 'key2' => 'value2'],
825
                true,
826
            ],
827
            [
828
                [0 => 'value', 1 => 'value2', 'deux' => 'value3'],
829
                true,
830
            ],
831
            [
832
                [0 => 'value'],
833
                false,
834
            ],
835
            [
836
                [0 => 'value', 1 => 'value2', 3 => 'value3'],
837
                false,
838
            ],
839
            [
840
                ['0' => 'value', '1' => 'value2', '3' => 'value3'],
841
                false,
842
            ],
843
            [
844
                [],
845
                false,
846
            ],
847
        ];
848
    }
849
850
    public function testCleanupEmptyTemporaryFiles(): void
851
    {
852
        $generator = $this->getMockBuilder(AbstractGenerator::class)
853
            ->setMethods([
854
                'configure',
855
                'unlink',
856
            ])
857
            ->setConstructorArgs(['the_binary'])
858
            ->getMock()
859
        ;
860
861
        $generator
862
            ->expects($this->once())
863
            ->method('unlink')
864
        ;
865
866
        $create = new ReflectionMethod($generator, 'createTemporaryFile');
867
        $create->setAccessible(true);
868
        $create->invoke($generator, null, null);
869
870
        $files = new ReflectionProperty($generator, 'temporaryFiles');
871
        $files->setAccessible(true);
872
        $this->assertCount(1, $files->getValue($generator));
873
874
        $remove = new ReflectionMethod($generator, 'removeTemporaryFiles');
875
        $remove->setAccessible(true);
876
        $remove->invoke($generator);
877
    }
878
879
    public function testleanupTemporaryFiles(): void
880
    {
881
        $generator = $this->getMockBuilder(AbstractGenerator::class)
882
            ->setMethods([
883
                'configure',
884
                'unlink',
885
            ])
886
            ->setConstructorArgs(['the_binary'])
887
            ->getMock()
888
        ;
889
890
        $generator
891
            ->expects($this->once())
892
            ->method('unlink')
893
        ;
894
895
        $create = new ReflectionMethod($generator, 'createTemporaryFile');
896
        $create->setAccessible(true);
897
        $create->invoke($generator, '<html/>', 'html');
898
899
        $files = new ReflectionProperty($generator, 'temporaryFiles');
900
        $files->setAccessible(true);
901
        $this->assertCount(1, $files->getValue($generator));
902
903
        $remove = new ReflectionMethod($generator, 'removeTemporaryFiles');
904
        $remove->setAccessible(true);
905
        $remove->invoke($generator);
906
    }
907
908
    public function testResetOptions(): void
909
    {
910
        $media = new class('/usr/local/bin/wkhtmltopdf') extends AbstractGenerator {
911
            protected function configure(): void
912
            {
913
                $this->addOptions([
914
                    'optionA' => null,
915
                    'optionB' => 'abc',
916
                ]);
917
            }
918
        };
919
920
        $media->setOption('optionA', 'bar');
921
922
        $this->assertEquals(
923
            [
924
                'optionA' => 'bar',
925
                'optionB' => 'abc',
926
            ],
927
            $media->getOptions()
928
        );
929
930
        $media->resetOptions();
931
932
        $this->assertEquals(
933
            [
934
                'optionA' => null,
935
                'optionB' => 'abc',
936
            ],
937
            $media->getOptions()
938
        );
939
    }
940
941
    /**
942
     * @return null|string
943
     */
944
    private function getPHPExecutableFromPath(): ?string
945
    {
946
        if (isset($_SERVER['_'])) {
947
            return $_SERVER['_'];
948
        }
949
950
        if (@\defined(\PHP_BINARY)) {
951
            return \PHP_BINARY;
952
        }
953
954
        if (false === \getenv('PATH')) {
955
            return null;
956
        }
957
958
        $paths = \explode(\PATH_SEPARATOR, \getenv('PATH'));
959
        foreach ($paths as $path) {
960
            // we need this for XAMPP (Windows)
961
            if (\strstr($path, 'php.exe') && isset($_SERVER['WINDIR']) && \file_exists($path) && \is_file($path)) {
962
                return $path;
963
            }
964
            $php_executable = $path . \DIRECTORY_SEPARATOR . 'php' . (isset($_SERVER['WINDIR']) ? '.exe' : '');
965
            if (\file_exists($php_executable) && \is_file($php_executable)) {
966
                return $php_executable;
967
            }
968
        }
969
970
        return null; // not found
971
    }
972
}
973