Passed
Push — test ( d838cf...cef4a5 )
by Tom
03:29
created

BuilderTest::doSetUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\PharBuild;
6
7
use Ktomk\Pipelines\Lib;
8
use Ktomk\Pipelines\TestCase;
9
use ReflectionProperty;
10
11
/**
12
 * @covers \Ktomk\Pipelines\PharBuild\Builder
13
 */
14
class BuilderTest extends TestCase
15
{
16
    /**
17
     * @var string test phar archive file
18
     */
19
    private $file;
20
21
    /**
22
     * @var string old working directory
23
     */
24
    private $oldPw;
25
26
    /**
27
     * @var Builder
28
     */
29
    private $builder;
30
31
    public function doSetUp()
32
    {
33
        parent::doSetUp();
34
        $this->file = sys_get_temp_dir() . '/ptst.phar';
35
        $this->oldPw = getcwd();
36
    }
37
38
    public function doTearDown()
39
    {
40
        if (file_exists($this->file)) {
41
            unlink($this->file);
42
        }
43
        if ($this->oldPw && $this->oldPw !== getcwd()) {
44
            chdir($this->oldPw);
45
        }
46
47
        parent::doTearDown();
48
    }
49
50
    public function testCreation()
51
    {
52
        $builder = Builder::create('fake.phar');
53
        self::assertInstanceOf('Ktomk\Pipelines\PharBuild\Builder', $builder);
54
    }
55
56
    /**
57
     */
58
    public function testIllegalStub()
59
    {
60
        $this->expectException('PharException');
61
        $this->expectExceptionMessageMatches('~^illegal stub for phar "/[a-zA-Z0-9/._]+\\.phar"$~');
62
63
        $this->needsPharWriteAccess();
64
65
        $builder = Builder::create('fake.phar');
66
        $builder->stubfile(__FILE__);
67
        $builder->add(basename(__FILE__), null, __DIR__);
68
        $builder->build();
69
    }
70
71
    /**
72
     * php 5.3 crashes with this test (exit status 255)
73
     *
74
     * @requires PHP 5.4.0
75
     */
76
    public function testDropFirstLineCallbackFileReadError()
77
    {
78
        $this->builder = $builder = Builder::create('fake.phar');
79
        $this->assertFNE($builder);
80
        $this->expectOutputString("error reading file: data:no-comma-in-URL\n");
81
        $builder->errHandle = fopen('php://output', 'wb');
0 ignored issues
show
Documentation Bug introduced by
It seems like fopen('php://output', 'wb') can also be of type false. However, the property $errHandle is declared as type null|resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
82
        $callback = $builder->dropFirstLine();
83
        self::assertIsCallable($callback);
84
        // violate rfc2397 by intention to provoke read error
85
        $actual = @call_user_func($callback, 'data:no-comma-in-URL');
86
        self::assertNull($actual);
87
    }
88
89
    public function testReplaceFileCallback()
90
    {
91
        $this->builder = $builder = Builder::create('fake.phar');
92
        $this->assertFNE($builder);
93
        $callback = $builder->replace('abc', '123');
94
        self::assertIsCallable($callback);
95
        $result = call_user_func($callback, 'data:,abc');
96
        self::assertArrayHasKey(0, $result);
97
        self::assertArrayHasKey(1, $result);
98
        self::assertSame('str', $result[0]);
99
        self::assertIsString($result[1]);
100
        self::assertSame('123', $result[1]);
101
    }
102
103
    /**
104
     * @throws \ReflectionException
105
     */
106
    public function testBuildFilesInvalidType()
107
    {
108
        $this->expectException('UnexpectedValueException');
109
        $this->expectExceptionMessage('unknown type: type');
110
111
        $this->needsPharWriteAccess();
112
        $this->builder = $builder = Builder::create('fake.phar');
113
        $this->assertFNE($builder);
114
115
        $filesProperty = new ReflectionProperty($builder, 'files');
116
        $filesProperty->setAccessible(true);
117
        $filesProperty->setValue($builder, array('.null' => array('type', 'context')));
118
119
        $builder->build();
120
    }
121
122
    /**
123
     * test phpExec() will trigger which resolution and PHP_BINARY mapping
124
     */
125
    public function testPhpExec()
126
    {
127
        $testCase = $this;
128
        $expected = sprintf('~^%s -f (?:/usr)?/bin/test -- $~', preg_quote(Lib::phpBinary(), '~'));
129
130
        $builder = $this->createPartialMock('Ktomk\Pipelines\PharBuild\Builder', array('exec'));
131
        $builder
132
            ->method('exec')
133
            ->willReturnCallback(function ($command, &$return) use ($testCase, $expected, $builder) {
134
                $testCase::assertMatchesRegularExpression($expected, $command);
135
                $return = 'OK';
136
137
                return $builder;
138
            });
139
140
        self::assertSame($builder, $builder->phpExec('test', $return));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return seems to be never defined.
Loading history...
141
        self::assertSame('OK', $return);
142
    }
143
144
    public function testPhpExecWithInvalidCommand()
145
    {
146
        $this->builder = $builder = Builder::create('fake.phar');
147
        $this->assertFNE($builder);
148
        $this->expectOutputString("php command error: unable to resolve \"not-existing-fake-utility\", verify the file exists and it is an actual php utility\n");
149
        $builder->errHandle = fopen('php://output', 'wb');
0 ignored issues
show
Documentation Bug introduced by
It seems like fopen('php://output', 'wb') can also be of type false. However, the property $errHandle is declared as type null|resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
150
        $builder->phpExec('not-existing-fake-utility');
151
    }
152
153
    private function assertFNE(Builder $actual)
154
    {
155
        $builder = $this->builder;
156
        self::assertInstanceOf('Ktomk\Pipelines\PharBuild\Builder', $actual);
157
        self::assertSame($builder, $actual, 'the same builder');
158
        self::assertCount(0, $actual->errors(), 'zero errors');
159
    }
160
161
    private function needsPharWriteAccess()
162
    {
163
        if ('0' !== ini_get('phar.readonly')) {
164
            self::markTestSkipped('phar.readonly is active');
165
        }
166
    }
167
}
168