Passed
Push — main ( 2c83d5...fb3f8a )
by Siad
05:21
created

ApplyTaskTest   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 499
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 54
eloc 125
dl 0
loc 499
rs 6.4799
c 0
b 0
f 0

42 Methods

Rating   Name   Duplication   Size   Complexity  
A testReturnProperty() 0 4 1
A testPassThru() 0 4 1
A testPropertySetPassthru() 0 3 1
A testDoNotExecuteOnWrongOs() 0 8 1
A testPropertySetOutputProperty() 0 3 1
A testPropertySetAddsourcefile() 0 3 1
A testPropertySetError() 0 5 1
A testPropertySetRelative() 0 3 1
A testExecuteOnCorrectOs() 0 4 1
A testEscapedArg() 0 4 2
A testEscape() 0 7 2
A getTargetByName() 0 8 3
A getTaskFromTarget() 0 12 4
A testOutputProperty() 0 4 1
A testSpawn() 0 10 1
A testPropertySetEscape() 0 3 1
A testPropertySetSpawn() 0 3 1
A testCheckreturnFalse() 0 3 1
A testOutputAppend() 0 14 2
A testNestedArgs() 0 4 1
A testMissingExecutable() 0 3 1
A testPropertySetParallel() 0 3 1
A testCheckreturnTrue() 0 4 1
A testPropertySetDir() 0 3 1
A testParallel() 0 8 2
A testRelativeSourceFilenames() 0 4 1
A testPropertySetCheckReturn() 0 3 1
A testSourceFilename() 0 5 1
A testMapperSupport() 0 14 2
A testPropertySetOs() 0 3 1
A testPropertySetReturnProperty() 0 3 1
A testPropertySetMaxparallel() 0 3 1
A assertAttributeIsSetTo() 0 8 2
A testFailOnNonExistingDir() 0 11 1
A testPropertySetAppend() 0 3 1
A testPropertySetForwardslash() 0 3 1
A setUp() 0 7 1
A testOutput() 0 14 1
A testChangeToDir() 0 4 1
A testError() 0 19 1
A getConfiguredTask() 0 11 2
A testPropertySetOutput() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like ApplyTaskTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ApplyTaskTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
namespace Phing\Test\Task\System;
21
22
use Exception;
23
use Phing\Io\File;
24
use Phing\Io\FileUtils;
25
use Phing\Target;
26
use Phing\Task;
27
use Phing\Task\System\ApplyTask;
28
use Phing\Test\Support\BuildFileTest;
29
use Phing\UnknownElement;
30
use ReflectionProperty;
31
32
/**
33
 * Tests the Apply Task
34
 *
35
 * @author  Utsav Handa <handautsav at hotmail dot com>
36
 */
37
class ApplyTaskTest extends BuildFileTest
38
{
39
    /**
40
     * Whether test is being run on windows
41
     * @var bool
42
     */
43
    protected $windows;
44
45
    /**
46
     * Setup the test
47
     */
48
    public function setUp(): void
49
    {
50
        // Tests definitions
51
        $this->configureProject(PHING_TEST_BASE . '/etc/tasks/system/ApplyTest.xml');
52
53
        // Identifying the running environment
54
        $this->windows = strtoupper(substr(PHP_OS, 0, 3)) == 'WIN';
55
    }
56
57
    /**********************************************************************************/
58
    /************************************** T E S T S *********************************/
59
    /**********************************************************************************/
60
61
    /**
62
     * Tests the OS configuration setting
63
     */
64
    public function testPropertySetOs()
65
    {
66
        $this->assertAttributeIsSetTo('os', 'linux');
67
    }
68
69
    /**
70
     * Tests the dir configuration setting
71
     */
72
    public function testPropertySetDir()
73
    {
74
        $this->assertAttributeIsSetTo('dir', new File($this->project->getProperty('php.tmpdir')));
75
    }
76
77
    /**
78
     * Tests the escape configuration setting
79
     */
80
    public function testPropertySetEscape()
81
    {
82
        $this->assertAttributeIsSetTo('escape', true);
83
    }
84
85
    /**
86
     * Tests the pass-thru configuration setting
87
     */
88
    public function testPropertySetPassthru()
89
    {
90
        $this->assertAttributeIsSetTo('passthru', true);
91
    }
92
93
    /**
94
     * Tests the spawn configuration setting
95
     */
96
    public function testPropertySetSpawn()
97
    {
98
        $this->assertAttributeIsSetTo('spawn', true);
99
    }
100
101
    /**
102
     * Tests the returnProperty configuration setting
103
     */
104
    public function testPropertySetReturnProperty()
105
    {
106
        $this->assertAttributeIsSetTo('returnProperty', 'retval');
107
    }
108
109
    /**
110
     * Tests the outputProperty configuration setting
111
     */
112
    public function testPropertySetOutputProperty()
113
    {
114
        $this->assertAttributeIsSetTo('outputProperty', 'outval');
115
    }
116
117
    /**
118
     * Tests the checkReturn/failonerror configuration setting
119
     */
120
    public function testPropertySetCheckReturn()
121
    {
122
        $this->assertAttributeIsSetTo('checkreturn', true);
123
    }
124
125
    /**
126
     * Tests the output configuration setting
127
     */
128
    public function testPropertySetOutput()
129
    {
130
        $this->assertAttributeIsSetTo(
131
            'output',
132
            new File($this->project->getProperty('php.tmpdir') . '/outputfilename')
133
        );
134
    }
135
136
    /**
137
     * Tests the error configuration setting
138
     */
139
    public function testPropertySetError()
140
    {
141
        $this->assertAttributeIsSetTo(
142
            'error',
143
            new File($this->project->getProperty('php.tmpdir') . '/errorfilename')
144
        );
145
    }
146
147
    /**
148
     * Tests the append configuration setting
149
     */
150
    public function testPropertySetAppend()
151
    {
152
        $this->assertAttributeIsSetTo('append', true, 'appendoutput');
153
    }
154
155
    /**
156
     * Tests the parallel configuration setting
157
     */
158
    public function testPropertySetParallel()
159
    {
160
        $this->assertAttributeIsSetTo('parallel', false);
161
    }
162
163
    /**
164
     * Tests the addsourcefile configuration setting
165
     */
166
    public function testPropertySetAddsourcefile()
167
    {
168
        $this->assertAttributeIsSetTo('addsourcefile', false);
169
    }
170
171
    /**
172
     * Tests the relative configuration setting
173
     */
174
    public function testPropertySetRelative()
175
    {
176
        $this->assertAttributeIsSetTo('relative', false);
177
    }
178
179
    /**
180
     * Tests the forwardslash configuration setting
181
     */
182
    public function testPropertySetForwardslash()
183
    {
184
        $this->assertAttributeIsSetTo('forwardslash', true);
185
    }
186
187
    /**
188
     * Tests the maxparallel configuration setting
189
     */
190
    public function testPropertySetMaxparallel()
191
    {
192
        $this->assertAttributeIsSetTo('maxparallel', 10);
193
    }
194
195
    /**
196
     * Tests the OS execution for the unspecified OS
197
     */
198
    public function testDoNotExecuteOnWrongOs()
199
    {
200
201
        // Process
202
        $this->executeTarget(__FUNCTION__);
203
        $this->assertInLogs('was not found in the specified list of valid OSes: unknownos');
204
205
        $this->assertStringNotContainsString('this should not be executed', $this->getOutput());
206
    }
207
208
    /**
209
     * Tests the OS execution for the specified OS list
210
     */
211
    public function testExecuteOnCorrectOs()
212
    {
213
        $this->executeTarget(__FUNCTION__);
214
        $this->assertInLogs('this should be executed');
215
    }
216
217
    /**
218
     * Tests the dir changing on a non-existent directory
219
     */
220
    public function testFailOnNonExistingDir()
221
    {
222
        $nonExistentDir = $this->project->getProperty('php.tmpdir') . DIRECTORY_SEPARATOR
223
            . 'non' . DIRECTORY_SEPARATOR
224
            . 'existent' . DIRECTORY_SEPARATOR
225
            . 'dir';
226
227
        return $this->expectBuildExceptionContaining(
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->expectBuildExcept...not a valid directory') targeting Phing\Test\Support\Build...ldExceptionContaining() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
228
            __FUNCTION__,
229
            __FUNCTION__,
230
            "'$nonExistentDir' is not a valid directory"
231
        );
232
    }
233
234
    /**
235
     * Tests the dir changing on an existent directory
236
     *
237
     * @requires OS ^(?:(?!Win).)*$
238
     */
239
    public function testChangeToDir()
240
    {
241
        $this->executeTarget(__FUNCTION__);
242
        $this->assertInLogs('Working directory change successful');
243
    }
244
245
    /**
246
     * Tests the failonerror/checkreturn value for 'true'
247
     *
248
     * @requires OS ^(?:(?!Win).)*$
249
     */
250
    public function testCheckreturnTrue()
251
    {
252
        $this->executeTarget(__FUNCTION__);
253
        $this->assertTrue(true);
254
    }
255
256
    /**
257
     * Tests the failonerror/checkreturn value for 'false'
258
     *
259
     * @requires OS ^(?:(?!Win).)*$
260
     */
261
    public function testCheckreturnFalse()
262
    {
263
        return $this->expectBuildExceptionContaining(__FUNCTION__, __FUNCTION__, 'Task exited with code (1)');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->expectBuildExcept... exited with code (1)') targeting Phing\Test\Support\Build...ldExceptionContaining() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
264
    }
265
266
    /**
267
     * Tests the outputProperty setting
268
     */
269
    public function testOutputProperty()
270
    {
271
        $this->executeTarget(__FUNCTION__);
272
        $this->assertInLogs('The output property\'s value is: "foo"');
273
    }
274
275
    /**
276
     * Tests the returnProperty setting
277
     */
278
    public function testReturnProperty()
279
    {
280
        $this->executeTarget(__FUNCTION__);
281
        $this->assertInLogs('The return property\'s value is: "1"');
282
    }
283
284
    /**
285
     * Tests the command escaping for execution
286
     */
287
    public function testEscape()
288
    {
289
        $this->executeTarget(__FUNCTION__);
290
        $this->assertInLogs(
291
            $this->windows
292
                ? (escapeshellarg('echo') . ' ' . escapeshellarg('foo') . " " . escapeshellarg('|') . " " . escapeshellarg('cat'))
293
                : ("'echo' 'foo' '|' 'cat'")
294
        );
295
    }
296
297
    /**
298
     * Tests the command execution with 'passthru' function
299
     */
300
    public function testPassThru()
301
    {
302
        $this->executeTarget(__FUNCTION__);
303
        $this->assertInLogs('Executing command:');
304
    }
305
306
    /**
307
     * Tests the output file functionality
308
     */
309
    public function testOutput()
310
    {
311
312
        // Getting a temp. file
313
        $tempfile = tempnam(FileUtils::getTempDir(), 'phing-exectest-');
314
315
        // Setting the property
316
        $this->project->setProperty('execTmpFile', $tempfile);
317
        $this->executeTarget(__FUNCTION__);
318
319
        // Validating the output
320
        $output = @file_get_contents($tempfile);
321
        @unlink($tempfile);
322
        $this->assertEquals('outfoo', rtrim($output));
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type false; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

322
        $this->assertEquals('outfoo', rtrim(/** @scrutinizer ignore-type */ $output));
Loading history...
323
    }
324
325
    /**
326
     * Tests the error file functionality
327
     *
328
     * @requires OS ^(?:(?!Win).)*$
329
     */
330
    public function testError()
331
    {
332
        // Getting a temp. file
333
        $tempfile = tempnam(FileUtils::getTempDir(), 'phing-exectest-');
334
335
        $scriptFile = getcwd() . "/error_output.sh";
336
        file_put_contents($scriptFile, "echo errfoo 1>&2");
337
        chmod($scriptFile, 0744);
338
339
        // Setting the property
340
        $this->project->setProperty('executable', $scriptFile);
341
        $this->project->setProperty('execTmpFile', $tempfile);
342
        $this->executeTarget(__FUNCTION__);
343
344
        // Validating the output
345
        $output = @file_get_contents($tempfile);
346
        @unlink($tempfile);
347
        @unlink($scriptFile);
348
        $this->assertEquals("errfoo", rtrim($output));
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type false; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

348
        $this->assertEquals("errfoo", rtrim(/** @scrutinizer ignore-type */ $output));
Loading history...
349
    }
350
351
    /**
352
     * Tests the execution with the background process spawning
353
     *
354
     * @requires OS ^(?:(?!Win).)*$
355
     */
356
    public function testSpawn()
357
    {
358
        // Process
359
        $start = time();
360
        $this->executeTarget(__FUNCTION__);
361
        $end = time();
362
        $this->assertLessThan(
363
            4,
364
            ($end - $start),
365
            'Execution time should be lower than 4 seconds, otherwise spawning did not work'
366
        );
367
    }
368
369
    /**
370
     * Tests the nested arguments specified for the execution
371
     */
372
    public function testNestedArgs()
373
    {
374
        $this->executeTarget(__FUNCTION__);
375
        $this->assertInLogs('echo Hello World');
376
    }
377
378
    /**
379
     * Tests the missing/unspecified executable information
380
     */
381
    public function testMissingExecutable()
382
    {
383
        $this->expectBuildExceptionContaining(__FUNCTION__, __FUNCTION__, 'Please provide "executable" information');
384
    }
385
386
    /**
387
     * Tests the escape functionality for special characters in argument
388
     */
389
    public function testEscapedArg()
390
    {
391
        $this->executeTarget(__FUNCTION__);
392
        $this->assertPropertyEquals('outval', $this->windows ? 'abc$b3 SB' : 'abc$b3!SB');
393
    }
394
395
    /**
396
     * Tests the relative source filenames functionality
397
     *
398
     * @requires OS ^(?:(?!Win).)*$
399
     */
400
    public function testRelativeSourceFilenames()
401
    {
402
        $this->executeTarget(__FUNCTION__);
403
        $this->assertNotInLogs('/etc/');
404
    }
405
406
    /**
407
     * Tests the source filename addition functionality
408
     *
409
     * @requires OS ^(?:(?!Win).)*$
410
     */
411
    public function testSourceFilename()
412
    {
413
        $this->executeTarget(__FUNCTION__);
414
        // As the addsourcefilename is 'off', only the executable should be processed in the execution
415
        $this->assertInLogs('Executing command: ls');
416
    }
417
418
    /**
419
     * Tests the output file append functionality
420
     */
421
    public function testOutputAppend()
422
    {
423
424
        // Getting a temp. file
425
        $tempfile = tempnam(FileUtils::getTempDir(), 'phing-exectest-');
426
427
        // Setting the property
428
        $this->project->setProperty('execTmpFile', $tempfile);
429
        $this->executeTarget(__FUNCTION__);
430
431
        // Validating the output
432
        $output = @file_get_contents($tempfile);
433
        @unlink($tempfile);
434
        $this->assertEquals($this->windows ? "Append OK \r\nAppend OK" : "Append OK\nAppend OK", rtrim($output));
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type false; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

434
        $this->assertEquals($this->windows ? "Append OK \r\nAppend OK" : "Append OK\nAppend OK", rtrim(/** @scrutinizer ignore-type */ $output));
Loading history...
435
    }
436
437
    /**
438
     * Tests the parallel configuration
439
     */
440
    public function testParallel()
441
    {
442
        $this->executeTarget(__FUNCTION__);
443
        $messages = [];
444
        foreach ($this->logBuffer as $log) {
445
            $messages[] = $log['message'];
446
        }
447
        $this->assertEquals(1, substr_count(implode("\n", $messages), 'Executing command:'));
448
    }
449
450
    public function testMapperSupport()
451
    {
452
        // Getting a temp. file
453
        $tempfile = tempnam(FileUtils::getTempDir(), 'phing-exectest-');
454
455
        // Setting the property
456
        $this->project->setProperty('execTmpFile', $tempfile);
457
458
        $this->executeTarget(__FUNCTION__);
459
        $messages = [];
460
        foreach ($this->logBuffer as $log) {
461
            $messages[] = $log['message'];
462
        }
463
        $this->assertContains('Applied echo to 4 files and 0 directories.', $messages);
464
    }
465
466
    /**********************************************************************************/
467
    /************************** H E L P E R  M E T H O D S ****************************/
468
    /**********************************************************************************/
469
470
    /**
471
     * @param string $name
472
     * @return Target
473
     * @throws Exception
474
     */
475
    protected function getTargetByName($name)
476
    {
477
        foreach ($this->project->getTargets() as $target) {
478
            if ($target->getName() == $name) {
479
                return $target;
480
            }
481
        }
482
        throw new Exception(sprintf('Target "%s" not found', $name));
483
    }
484
485
    /**
486
     * @param string $target
487
     * @param string $taskName
488
     * @param int $pos
489
     * @return Task
490
     * @throws Exception
491
     */
492
    protected function getTaskFromTarget($target, $taskName, $pos = 0)
493
    {
494
        $rchildren = new ReflectionProperty(get_class($target), 'children');
0 ignored issues
show
Bug introduced by
$target of type string is incompatible with the type object expected by parameter $object of get_class(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

494
        $rchildren = new ReflectionProperty(get_class(/** @scrutinizer ignore-type */ $target), 'children');
Loading history...
495
        $rchildren->setAccessible(true);
496
        $n = -1;
497
        foreach ($rchildren->getValue($target) as $child) {
0 ignored issues
show
Bug introduced by
$target of type string is incompatible with the type null|object expected by parameter $object of ReflectionProperty::getValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

497
        foreach ($rchildren->getValue(/** @scrutinizer ignore-type */ $target) as $child) {
Loading history...
498
            if ($child instanceof Task && ++$n == $pos) {
499
                return $child;
500
            }
501
        }
502
503
        throw new Exception(sprintf('%s #%d not found in task', $taskName, $pos));
504
    }
505
506
    /**
507
     * @param string $target
508
     * @param string $task
509
     * @return Task
510
     */
511
    protected function getConfiguredTask($target, $task)
512
    {
513
        $target = $this->getTargetByName($target);
514
        $task = $this->getTaskFromTarget($target, $task);
515
        $task->maybeConfigure();
516
517
        if ($task instanceof UnknownElement) {
518
            return $task->getRuntimeConfigurableWrapper()->getProxy();
519
        }
520
521
        return $task;
522
    }
523
524
    /**
525
     * @param string $property
526
     * @param string $propertyName
527
     */
528
    protected function assertAttributeIsSetTo($property, $value, $propertyName = null)
529
    {
530
        $task = $this->getConfiguredTask('testPropertySet' . ucfirst($property), ApplyTask::class);
531
532
        $propertyName = ($propertyName === null) ? $property : $propertyName;
533
        $rprop = new ReflectionProperty(ApplyTask::class, $propertyName);
534
        $rprop->setAccessible(true);
535
        $this->assertEquals($value, $rprop->getValue($task));
536
    }
537
}
538