Completed
Push — master ( f9317c...c107ab )
by Vladimir
02:34
created

PHPUnit_Stakx_TestCase::generateFM()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Test;
9
10
use allejo\stakx\Command\BuildableCommand;
11
use allejo\stakx\Core\StakxLogger;
12
use allejo\stakx\Manager\CollectionManager;
13
use allejo\stakx\Service;
14
use allejo\stakx\System\Filesystem;
15
use allejo\stakx\System\Folder;
16
use org\bovigo\vfs\vfsStream;
17
use org\bovigo\vfs\vfsStreamDirectory;
18
use org\bovigo\vfs\vfsStreamFile;
19
use Psr\Log\LoggerInterface;
20
use Symfony\Component\Console\Output\ConsoleOutput;
21
use Symfony\Component\Yaml\Yaml;
22
23
abstract class PHPUnit_Stakx_TestCase extends \PHPUnit_Framework_TestCase
0 ignored issues
show
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
24
{
25
    const FM_OBJ_TEMPLATE = "---\n%s\n---\n\n%s";
26
27
    /** @var string */
28
    protected $assetFolder;
29
30
    /**
31
     * @var vfsStreamFile
32
     */
33
    protected $dummyFile;
34
35
    /**
36
     * @var vfsStreamDirectory
37
     */
38
    protected $rootDir;
39
40
    /**
41
     * @var Filesystem
42
     */
43
    protected $fs;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fs. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
44
45
    public function setUp()
46
    {
47
        $this->dummyFile = vfsStream::newFile('stakx.html.twig');
48
        $this->rootDir = vfsStream::setup();
49
        $this->fs = new Filesystem();
50
51
        Service::setParameter(BuildableCommand::USE_DRAFTS, false);
52
        Service::setParameter(BuildableCommand::WATCHING, false);
53
54
        // Inspect the VFS as an array
55
        // vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure();
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
56
    }
57
58
    ///
59
    // Assertion functions
60
    ///
61
62
    /**
63
     * @param string $haystack
64
     */
65
    protected function assertStringContains($needle, $haystack, $message = '')
66
    {
67
        $this->assertNotFalse(strpos($haystack, $needle), $message);
68
    }
69
70
    protected function assertFileContains($fileContent, $filePath, $message = '')
71
    {
72
        (substr($filePath, -1, 1) == '/') && $filePath .= 'index.html';
73
74
        $contents = file_get_contents($filePath);
75
76
        $this->assertStringContains($fileContent, $contents, $message);
77
    }
78
79
    ///
80
    // Utility Functions
81
    ///
82
83
    protected function bookCollectionProvider($jailed = false)
84
    {
85
        $cm = new CollectionManager();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $cm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
86
        $cm->setLogger($this->getMockLogger());
87
        $cm->parseCollections(array(
88
            array(
89
                'name' => 'books',
90
                'folder' => 'tests/allejo/stakx/Test/assets/MyBookCollection/',
91
            ),
92
        ));
93
94
        return (!$jailed) ? $cm->getCollections() : $cm->getJailedCollections();
95
    }
96
97
    /**
98
     * Write a temporary file to the asset folder
99
     *
100
     * @param $fileName
101
     * @param $content
102
     *
103
     * @return string Path to the temporary file; relative to the project's root
104
     */
105
    protected function createTempFile($fileName, $content)
106
    {
107
        $folder = new Folder($this->assetFolder);
108
        $folder->writeFile($fileName, $content);
109
110
        return $this->fs->appendPath($this->assetFolder, $fileName);
111
    }
112
113
    protected function createBlankFile($filename, $classType, $content)
114
    {
115
        $file = vfsStream::newFile($filename);
116
        $file
117
            ->setContent($content)
118
            ->at($this->rootDir);
119
120
        return new $classType($file->url());
121
    }
122
123
    protected function createVirtualFilePath($frontMatter = array(), $body = 'Body Text')
124
    {
125
        $this->dummyFile
126
            ->setContent($this->generateFM($frontMatter, $body))
127
            ->at($this->rootDir);
128
129
        return $this->dummyFile->url();
130
    }
131
132
    /**
133
     * @param string $classType
134
     * @param array  $frontMatter
135
     * @param string $body
136
     *
137
     * @return mixed
138
     */
139
    protected function createVirtualFile($classType, $frontMatter = array(), $body = 'Body Text')
140
    {
141
        return new $classType($this->createVirtualFilePath($frontMatter, $body));
142
    }
143
144
    protected function createMultipleVirtualFiles($classType, $elements)
145
    {
146
        $results = array();
147
148
        foreach ($elements as $element)
149
        {
150
            $filename = (isset($element['filename'])) ? $element['filename'] : hash('sha256', uniqid(mt_rand(), true), false);
151
            $frontMatter = (empty($element['frontmatter'])) ? '' : Yaml::dump($element['frontmatter'], 2);
152
            $body = (isset($element['body'])) ? $element['body'] : 'Body Text';
153
154
            $file = vfsStream::newFile($filename);
155
            $file
156
                ->setContent(sprintf("---\n%s\n---\n\n%s", $frontMatter, $body))
157
                ->at($this->rootDir);
158
159
            $results[] = new $classType($file->url());
160
        }
161
162
        return $results;
163
    }
164
165
    /**
166
     * Get a mock logger.
167
     *
168
     * @return LoggerInterface
169
     */
170
    protected function getMockLogger()
171
    {
172
        return $this->getMock(LoggerInterface::class);
173
    }
174
175
    /**
176
     * Get a real logger instance that will save output to the console.
177
     *
178
     * @return StakxLogger
179
     */
180
    protected function getReadableLogger()
181
    {
182
        stream_filter_register('intercept', StreamInterceptor::class);
183
        $stakxLogger = new StakxLogger(new ConsoleOutput());
184
        stream_filter_append($stakxLogger->getOutputInterface()->getStream(), 'intercept');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Output\OutputInterface as the method getStream() does only exist in the following implementations of said interface: Symfony\Component\Console\Output\ConsoleOutput, Symfony\Component\Console\Output\StreamOutput.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
185
186
        return $stakxLogger;
187
    }
188
189
    /**
190
     * Generate a FrontMatter-ready syntax to be used as a file's content.
191
     *
192
     * @param array  $frontMatter
193
     * @param string $body
194
     *
195
     * @return string
196
     */
197
    protected function generateFM(array $frontMatter = array(), $body = 'Body text')
198
    {
199
        $fm = (empty($frontMatter)) ? '' : Yaml::dump($frontMatter, 2);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $fm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
200
201
        return sprintf(self::FM_OBJ_TEMPLATE, $fm, $body);
202
    }
203
204
    /**
205
     * Create a temporary folder where temporary file writes will be made to
206
     *
207
     * Remember to remove the folder in during the ::tearDown()
208
     *
209
     * @param string $folderName
210
     */
211
    protected function createAssetFolder($folderName)
212
    {
213
        $this->assetFolder = $this->fs->getRelativePath($this->fs->appendPath(__DIR__, $folderName));
214
215
        $this->fs->mkdir($this->assetFolder);
216
    }
217
}
218