Passed
Push — master ( 2ed18b...d69bd8 )
by Caen
04:33 queued 17s
created

IntegrationTestCase::setUpBeforeClass()   C

Complexity

Conditions 15
Paths 113

Size

Total Lines 63
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 31
c 2
b 0
f 0
nc 113
nop 0
dl 0
loc 63
rs 5.8083

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\RealtimeCompiler\Tests\Integration;
6
7
use InvalidArgumentException;
8
use PHPUnit\Framework\TestCase;
9
use RuntimeException;
10
use ZipArchive;
11
12
abstract class IntegrationTestCase extends TestCase
13
{
14
    /** @var resource */
15
    protected static $server;
16
17
    public static function setUpBeforeClass(): void
18
    {
19
        $monorepo = realpath(__DIR__.'/../../../../');
20
21
        if ($monorepo && realpath(getcwd()) === $monorepo && file_exists($monorepo.'/hyde')) {
22
            throw new InvalidArgumentException('This test suite is not intended to be run from the monorepo.');
23
        }
24
25
        if (! self::hasTestRunnerSetUp()) {
26
            self::setUpTestRunner();
27
        }
28
29
        // Check that post 8080 is available
30
        $process = @fsockopen('localhost', 8080, $errno, $errstr, 1);
31
32
        if ($process) {
33
            // Try to find the PID of the process using the port
34
            if (PHP_OS_FAMILY === 'Windows') {
35
                $raw = shell_exec('netstat -aon | findstr :8080');
36
                // Get the PID of the process (last column of the first line)
37
                preg_match('/\s+(\d+)$/', explode("\n", $raw)[0], $matches);
38
                $pid = trim($matches[1]);
39
            } else {
40
                $pid = shell_exec('lsof -t -i:8080');
41
            }
42
43
            fclose($process);
44
45
            $throwOnBusyPort = false;
46
            if ($throwOnBusyPort) {
47
                throw new RuntimeException(sprintf('Port 8080 is already in use. (PID %s)', $pid));
48
            } else {
49
                // Kill the process using the port
50
                shell_exec(PHP_OS_FAMILY === 'Windows' ? "taskkill /F /PID $pid" : "kill -9 $pid");
51
            }
52
        }
53
54
        // Start the server in a background process, keeping the task ID for later
55
        $null = PHP_OS_FAMILY === 'Windows' ? 'NUL' : '/dev/null';
56
        self::$server = proc_open("php hyde serve > $null", [], $pipes, realpath(self::getRunnerPath()));
57
58
        // Wait for the server to start
59
        $waitTime = time();
60
        while (@fsockopen('localhost', 8080, $errno, $errstr, 1) === false) {
61
            // Timeout after 5 seconds
62
            if (time() - $waitTime > 5) {
63
                throw new RuntimeException('Failed to start the test server.');
64
            }
65
66
            if (proc_get_status(self::$server)['running'] === false) {
67
                // Make a head request to the server to see if it's running
68
                if (shell_exec('curl -I http://localhost:8080') === false) {
69
                    break;
70
                }
71
            }
72
73
            // Sleep 20ms
74
            usleep(20000);
75
        }
76
77
        // Assert that the server was started successfully
78
        if (! self::$server) {
79
            throw new RuntimeException('Failed to start the test server.');
80
        }
81
    }
82
83
    protected static function getRunnerPath(): string
84
    {
85
        // Get path from the environment variable
86
        $path = getenv('HYDE_RC_RUNNER_PATH');
87
88
        if ($path === false) {
89
            throw new RuntimeException('HYDE_RC_RUNNER_PATH environment variable is not set.');
90
        }
91
92
        // Check that it's not a child of the project root
93
        $packageDir = realpath(__DIR__.'/../../');
94
        if (str_starts_with($path, $packageDir)) {
95
            throw new RuntimeException('HYDE_RC_RUNNER_PATH cannot be a child of the package root as junctioning will massivly inflate vendor directory.');
96
        }
97
98
        if (file_exists($path)) {
99
            $path = realpath($path);
100
        }
101
102
        return $path;
103
    }
104
105
    public function __destruct()
106
    {
107
        // Ensure the server is stopped, regardless of any errors
108
        if (self::$server) {
109
            proc_terminate(self::$server);
110
        }
111
    }
112
113
    protected static function hasTestRunnerSetUp(): bool
114
    {
115
        return file_exists(self::getRunnerPath()) && file_exists(self::getRunnerPath().'/vendor/autoload.php');
116
    }
117
118
    public static function setUpTestRunner(): void
119
    {
120
        echo "\33[33mSetting up test runner...\33[0m This may take a while.\n";
121
122
        $archive = 'https://github.com/hydephp/hyde/archive/refs/heads/master.zip';
123
        $target = self::getRunnerPath();
124
        if (file_exists($target)) {
125
            rmdir($target);
126
        }
127
128
        echo "\33[33mDownloading test runner scaffolding...\33[0m\n";
129
        $raw = file_get_contents($archive);
130
131
        if ($raw === false) {
132
            throw new RuntimeException('Failed to download test runner.');
133
        }
134
135
        echo "\33[33mExtracting archive...\33[0m\n";
136
        $zipPath = tempnam(sys_get_temp_dir(), 'hyde-master');
137
138
        if ($zipPath === false) {
139
            throw new RuntimeException('Failed to create temporary file.');
140
        }
141
142
        file_put_contents($zipPath, $raw);
143
144
        $zip = new ZipArchive();
145
146
        if ($zip->open($zipPath) !== true) {
147
            throw new RuntimeException('Failed to open zip archive.');
148
        }
149
150
        // Get the name of the root directory in the zip file
151
        $rootDir = $zip->getNameIndex(0);
152
153
        // Extract to a temporary directory
154
        $tempExtractPath = $target.'_temp';
155
        $zip->extractTo($tempExtractPath);
156
157
        $zip->close();
158
159
        // Move the contents of the extracted directory to the target directory
160
        rename($tempExtractPath.'/'.$rootDir, $target);
161
162
        // Remove the temporary extraction directory
163
        rmdir($tempExtractPath);
164
165
        unlink($zipPath);
166
167
        $runner = realpath($target);
168
169
        if ($runner === false) {
170
            throw new RuntimeException('Failed to get the real path of the test runner.');
171
        }
172
173
        $workDir = getcwd();
174
175
        // Junction the package source of hyde/realtime-compiler to the test runner
176
        $branch = getenv('HYDE_RC_BRANCH') ?: trim(shell_exec('git rev-parse --abbrev-ref HEAD') ?: 'master');
177
        echo "\33[33mInstalling hyde/realtime-compiler:dev-$branch...\33[0m\n";
178
        chdir($runner);
179
        exec('composer config repositories.realtime-compiler path '.realpath(__DIR__.'/../../'), $output, $return);
180
        if ($return !== 0) {
181
            throw new RuntimeException('Failed to add repository path.');
182
        }
183
        exec("composer require --dev hyde/realtime-compiler:dev-$branch --no-progress", $output, $return);
184
        if ($return !== 0) {
185
            throw new RuntimeException('Failed to install hyde/realtime-compiler.');
186
        }
187
        chdir($workDir);
188
    }
189
190
    public function projectPath(string $path = ''): string
191
    {
192
        return realpath(self::getRunnerPath()).($path ? '/'.$path : '');
193
    }
194
195
    public function get(string $uri): TestResponse
196
    {
197
        return TestResponse::get($this, $uri);
198
    }
199
}
200