• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orchestral / testbench-core / 12324437332

13 Dec 2024 11:13PM UTC coverage: 91.777% (-0.5%) from 92.296%
12324437332

Pull #278

github

web-flow
Merge 82ecf708a into b1b7dd073
Pull Request #278: [7.x] Fixes database seeding using `WithWorkbench` and `RefreshDatabase`

6 of 14 new or added lines in 1 file covered. (42.86%)

25 existing lines in 8 files now uncovered.

1250 of 1362 relevant lines covered (91.78%)

64.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.22
/src/functions.php
1
<?php
2

3
namespace Orchestra\Testbench;
4

5
use Closure;
6
use Illuminate\Contracts\Console\Kernel as ConsoleKernel;
7
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
8
use Illuminate\Filesystem\Filesystem;
9
use Illuminate\Foundation\Application;
10
use Illuminate\Routing\Router;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Collection;
13
use Illuminate\Support\ProcessUtils;
14
use Illuminate\Support\Str;
15
use Illuminate\Testing\PendingCommand;
16
use InvalidArgumentException;
17
use Orchestra\Testbench\Foundation\Env;
18
use PHPUnit\Runner\Version;
19
use RuntimeException;
20
use Symfony\Component\Process\Process;
21

22
/**
23
 * Create Laravel application instance.
24
 *
25
 * @api
26
 *
27
 * @param  string|null  $basePath
28
 * @param  (callable(\Illuminate\Foundation\Application):(void))|null  $resolvingCallback
29
 * @param  array{extra?: array{providers?: array, dont-discover?: array, env?: array}, load_environment_variables?: bool, enabled_package_discoveries?: bool}  $options
30
 * @return \Orchestra\Testbench\Foundation\Application
31
 */
32
function container(?string $basePath = null, ?callable $resolvingCallback = null, array $options = []): Foundation\Application
33
{
34
    return Foundation\Application::make($basePath, $resolvingCallback, $options);
3✔
35
}
36

37
/**
38
 * Run artisan command.
39
 *
40
 * @api
41
 *
42
 * @param  \Orchestra\Testbench\Contracts\TestCase|\Illuminate\Contracts\Foundation\Application  $context
43
 * @param  string  $command
44
 * @param  array<string, mixed>  $parameters
45
 * @return int
46
 */
47
function artisan(Contracts\TestCase|ApplicationContract $context, string $command, array $parameters = []): int
48
{
49
    if ($context instanceof ApplicationContract) {
10✔
50
        return $context->make(ConsoleKernel::class)->call($command, $parameters);
1✔
51
    }
52

53
    $command = $context->artisan($command, $parameters);
10✔
54

55
    return $command instanceof PendingCommand ? $command->run() : $command;
10✔
56
}
57

58
/**
59
 * Run remote action using Testbench CLI.
60
 *
61
 * @api
62
 *
63
 * @param  array<int, string>|string  $command
64
 * @param  array<string, mixed>|string  $env
65
 * @param  bool|null  $tty
66
 * @return \Symfony\Component\Process\Process
67
 */
68
function remote(array|string $command, array|string $env = [], ?bool $tty = null): Process
69
{
70
    $binary = \defined('TESTBENCH_DUSK') ? 'testbench-dusk' : 'testbench';
10✔
71

72
    $commander = is_file($vendorBin = package_path('vendor', 'bin', $binary))
10✔
73
        ? ProcessUtils::escapeArgument((string) $vendorBin)
×
74
        : $binary;
10✔
75

76
    if (\is_string($env)) {
10✔
77
        $env = ['APP_ENV' => $env];
×
78
    }
79

80
    Arr::add($env, 'TESTBENCH_PACKAGE_REMOTE', '(true)');
10✔
81

82
    $process = Process::fromShellCommandline(
10✔
83
        command: Arr::join([php_binary(true), $commander, ...Arr::wrap($command)], ' '),
10✔
84
        cwd: package_path(),
10✔
85
        env: array_merge(defined_environment_variables(), $env)
10✔
86
    );
10✔
87

88
    if (\is_bool($tty)) {
10✔
89
        $process->setTty($tty);
×
90
    }
91

92
    return $process;
10✔
93
}
94

95
/**
96
 * Run callback only once.
97
 *
98
 * @api
99
 *
100
 * @param  mixed  $callback
101
 * @return \Closure():mixed
102
 */
103
function once($callback): Closure
104
{
105
    $response = new Support\UndefinedValue;
164✔
106

107
    return function () use ($callback, &$response) {
164✔
108
        if ($response instanceof Support\UndefinedValue) {
164✔
109
            $response = value($callback) ?? null;
164✔
110
        }
111

112
        return $response;
164✔
113
    };
164✔
114
}
115

116
/**
117
 * Register after resolving callback.
118
 *
119
 * @api
120
 *
121
 * @param  \Illuminate\Contracts\Foundation\Application  $app
122
 * @param  string  $name
123
 * @param  (\Closure(object, \Illuminate\Contracts\Foundation\Application):(mixed))|null  $callback
124
 * @return void
125
 */
126
function after_resolving(ApplicationContract $app, string $name, ?Closure $callback = null): void
127
{
128
    $app->afterResolving($name, $callback);
164✔
129

130
    if ($app->resolved($name)) {
164✔
131
        value($callback, $app->make($name), $app);
5✔
132
    }
133
}
134

135
/**
136
 * Load migration paths.
137
 *
138
 * @api
139
 *
140
 * @param  \Illuminate\Contracts\Foundation\Application  $app
141
 * @param  array<int, string>|string  $paths
142
 * @return void
143
 */
144
function load_migration_paths(ApplicationContract $app, array|string $paths): void
145
{
146
    after_resolving($app, 'migrator', static function ($migrator) use ($paths) {
31✔
147
        foreach (Arr::wrap($paths) as $path) {
15✔
148
            /** @var \Illuminate\Database\Migrations\Migrator $migrator */
149
            $migrator->path($path);
15✔
150
        }
151
    });
31✔
152
}
153

154
/**
155
 * Get default environment variables.
156
 *
157
 * @return array<int, string>
158
 *
159
 * @deprecated
160
 *
161
 * @codeCoverageIgnore
162
 */
163
function default_environment_variables(): array
164
{
165
    return [];
166
}
167

168
/**
169
 * Get defined environment variables.
170
 *
171
 * @api
172
 *
173
 * @return array<string, mixed>
174
 */
175
function defined_environment_variables(): array
176
{
177
    return Collection::make(array_merge($_SERVER, $_ENV))
10✔
178
        ->keys()
10✔
179
        ->mapWithKeys(static fn (string $key) => [$key => Env::forward($key)])
10✔
180
        ->unless(
10✔
181
            Env::has('TESTBENCH_WORKING_PATH'), static fn ($env) => $env->put('TESTBENCH_WORKING_PATH', package_path())
10✔
182
        )->all();
10✔
183
}
184

185
/**
186
 * Get default environment variables.
187
 *
188
 * @api
189
 *
190
 * @param  iterable<string, mixed>  $variables
191
 * @return array<int, string>
192
 */
193
function parse_environment_variables($variables): array
194
{
195
    return Collection::make($variables)
4✔
196
        ->transform(static function ($value, $key) {
4✔
197
            if (\is_bool($value) || \in_array($value, ['true', 'false'])) {
4✔
198
                $value = \in_array($value, [true, 'true']) ? '(true)' : '(false)';
4✔
199
            } elseif (\is_null($value) || \in_array($value, ['null'])) {
1✔
200
                $value = '(null)';
1✔
201
            } else {
202
                $value = $key === 'APP_DEBUG' ? \sprintf('(%s)', Str::of($value)->ltrim('(')->rtrim(')')) : "'{$value}'";
1✔
203
            }
204

205
            return "{$key}={$value}";
4✔
206
        })->values()->all();
4✔
207
}
208

209
/**
210
 * Refresh router lookups.
211
 *
212
 * @api
213
 *
214
 * @param  \Illuminate\Routing\Router  $router
215
 * @return void
216
 */
217
function refresh_router_lookups(Router $router): void
218
{
219
    $router->getRoutes()->refreshNameLookups();
164✔
220
}
221

222
/**
223
 * Transform realpath to alias path.
224
 *
225
 * @api
226
 *
227
 * @param  string  $path
228
 * @param  string|null  $workingPath
229
 * @return string
230
 */
231
function transform_realpath_to_relative(string $path, ?string $workingPath = null, string $prefix = ''): string
232
{
233
    $separator = DIRECTORY_SEPARATOR;
13✔
234

235
    if (! \is_null($workingPath)) {
13✔
236
        return str_replace(rtrim($workingPath, $separator).$separator, $prefix.$separator, $path);
1✔
237
    }
238

239
    $laravelPath = base_path();
12✔
240
    $workbenchPath = workbench_path();
12✔
241
    $packagePath = package_path();
12✔
242

243
    return match (true) {
12✔
244
        str_starts_with($path, $laravelPath) => str_replace($laravelPath.$separator, '@laravel'.$separator, $path),
12✔
245
        str_starts_with($path, $workbenchPath) => str_replace($workbenchPath.$separator, '@workbench'.$separator, $path),
12✔
246
        str_starts_with($path, $packagePath) => str_replace($packagePath.$separator, '.'.$separator, $path),
12✔
247
        ! empty($prefix) => implode($separator, [$prefix, ltrim($path, $separator)]),
12✔
248
        default => $path,
12✔
249
    };
12✔
250
}
251

252
/**
253
 * Transform relative path.
254
 *
255
 * @api
256
 *
257
 * @param  string  $path
258
 * @param  string  $workingPath
259
 * @return string
260
 */
261
function transform_relative_path(string $path, string $workingPath): string
262
{
263
    return str_starts_with($path, './')
27✔
264
        ? rtrim($workingPath, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.mb_substr($path, 2)
1✔
265
        : $path;
27✔
266
}
267

268
/**
269
 * Get the default skeleton path.
270
 *
271
 * @api
272
 *
273
 * @param  array|string  $path
274
 * @return string
275
 */
276
function default_skeleton_path(array|string $path = ''): string
277
{
278
    return (string) realpath(join_paths(__DIR__, '..', 'laravel', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path)));
161✔
279
}
280

281
/**
282
 * Get the migration path by type.
283
 *
284
 * @api
285
 *
286
 * @param  string|null  $type
287
 * @return string
288
 *
289
 * @throws \InvalidArgumentException
290
 */
291
function default_migration_path(?string $type = null): string
292
{
293
    $path = realpath(
37✔
294
        \is_null($type) ? base_path('migrations') : base_path(join_paths('migrations', $type))
37✔
295
    );
37✔
296

297
    if ($path === false) {
37✔
UNCOV
298
        throw new InvalidArgumentException(\sprintf('Unable to resolve migration path for type [%s]', $type ?? 'laravel'));
×
299
    }
300

301
    return $path;
37✔
302
}
303

304
/**
305
 * Get the path to the package folder.
306
 *
307
 * @api
308
 *
309
 * @param  array|string  $path
310
 * @return string
311
 */
312
function package_path(array|string $path = ''): string
313
{
314
    $argumentCount = \func_num_args();
59✔
315

316
    $workingPath = \defined('TESTBENCH_WORKING_PATH')
59✔
317
        ? TESTBENCH_WORKING_PATH
25✔
318
        : Env::get('TESTBENCH_WORKING_PATH', getcwd());
34✔
319

320
    if ($argumentCount === 1 && \is_string($path) && str_starts_with($path, './')) {
59✔
UNCOV
321
        return transform_relative_path($path, $workingPath);
×
322
    }
323

324
    $path = join_paths(...Arr::wrap($argumentCount > 1 ? \func_get_args() : $path));
59✔
325

326
    return str_starts_with($path, './')
59✔
UNCOV
327
        ? transform_relative_path($path, $workingPath)
×
328
        : join_paths(rtrim($workingPath, DIRECTORY_SEPARATOR), $path);
59✔
329
}
330

331
/**
332
 * Get the workbench configuration.
333
 *
334
 * @api
335
 *
336
 * @return array<string, mixed>
337
 */
338
function workbench(): array
339
{
340
    /** @var \Orchestra\Testbench\Contracts\Config $config */
341
    $config = app()->bound(Contracts\Config::class)
28✔
342
        ? app()->make(Contracts\Config::class)
26✔
343
        : new Foundation\Config;
2✔
344

345
    return $config->getWorkbenchAttributes();
28✔
346
}
347

348
/**
349
 * Get the path to the workbench folder.
350
 *
351
 * @api
352
 *
353
 * @param  array|string  $path
354
 * @return string
355
 */
356
function workbench_path(array|string $path = ''): string
357
{
358
    return package_path('workbench', ...Arr::wrap(\func_num_args() > 1 ? \func_get_args() : $path));
46✔
359
}
360

361
/**
362
 * Get the migration path by type.
363
 *
364
 * @api
365
 *
366
 * @param  string|null  $type
367
 * @return string
368
 *
369
 * @throws \InvalidArgumentException
370
 *
371
 * @deprecated
372
 */
373
function laravel_migration_path(?string $type = null): string
374
{
375
    return default_migration_path($type);
1✔
376
}
377

378
/**
379
 * Determine if vendor symlink exists on the laravel application.
380
 *
381
 * @api
382
 *
383
 * @param  \Illuminate\Contracts\Foundation\Application  $app
384
 * @param  string|null  $workingPath
385
 * @return bool
386
 */
387
function laravel_vendor_exists(ApplicationContract $app, ?string $workingPath = null): bool
388
{
389
    $filesystem = new Filesystem;
3✔
390

391
    $appVendorPath = $app->basePath('vendor');
3✔
392
    $workingPath ??= package_path('vendor');
3✔
393

394
    return $filesystem->isFile(join_paths($appVendorPath, 'autoload.php')) &&
3✔
395
        $filesystem->hash(join_paths($appVendorPath, 'autoload.php')) === $filesystem->hash(join_paths($workingPath, 'autoload.php'));
3✔
396
}
397

398
/**
399
 * Laravel version compare.
400
 *
401
 * @api
402
 *
403
 * @param  string  $version
404
 * @param  string|null  $operator
405
 * @return int|bool
406
 */
407
function laravel_version_compare(string $version, ?string $operator = null)
408
{
409
    /** @phpstan-ignore identical.alwaysFalse */
410
    $laravel = Application::VERSION === '9.x-dev' ? '9.0.0' : Application::VERSION;
5✔
411

412
    if (\is_null($operator)) {
5✔
413
        return version_compare($laravel, $version);
1✔
414
    }
415

416
    return version_compare($laravel, $version, $operator);
5✔
417
}
418

419
/**
420
 * PHPUnit version compare.
421
 *
422
 * @api
423
 *
424
 * @param  string  $version
425
 * @param  string|null  $operator
426
 * @return int|bool
427
 *
428
 * @throws \RuntimeException
429
 */
430
function phpunit_version_compare(string $version, ?string $operator = null)
431
{
432
    if (! class_exists(Version::class)) {
3✔
UNCOV
433
        throw new RuntimeException('Unable to verify PHPUnit version');
×
434
    }
435

436
    if (\is_null($operator)) {
3✔
437
        return version_compare(Version::id(), $version);
1✔
438
    }
439

440
    return version_compare(Version::id(), $version, $operator);
3✔
441
}
442

443
/**
444
 * Determine the PHP Binary.
445
 *
446
 * @api
447
 *
448
 * @param  bool  $escape
449
 * @return string
450
 */
451
function php_binary(bool $escape = false): string
452
{
453
    $phpBinary = (new Support\PhpExecutableFinder)->find(false) ?: 'php';
10✔
454

455
    return $escape === true ? ProcessUtils::escapeArgument((string) $phpBinary) : $phpBinary;
10✔
456
}
457

458
/**
459
 * Join the given paths together.
460
 *
461
 * @param  string|null  $basePath
462
 * @param  string  ...$paths
463
 * @return string
464
 */
465
function join_paths(?string $basePath, string ...$paths): string
466
{
467
    foreach ($paths as $index => $path) {
167✔
468
        if (empty($path) && $path !== '0') {
167✔
469
            unset($paths[$index]);
161✔
470
        } else {
471
            $paths[$index] = DIRECTORY_SEPARATOR.ltrim($path, DIRECTORY_SEPARATOR);
167✔
472
        }
473
    }
474

475
    return $basePath.implode('', $paths);
167✔
476
}
477

478
/**
479
 * Ensure the provided `$app` return an instance of Laravel application or throw an exception.
480
 *
481
 * @internal
482
 *
483
 * @param  \Illuminate\Foundation\Application|null  $app
484
 * @param  string|null  $caller
485
 * @return \Illuminate\Foundation\Application
486
 *
487
 * @throws \Orchestra\Testbench\Exceptions\ApplicationNotAvailableException
488
 */
489
function laravel_or_fail($app, ?string $caller = null): Application
490
{
491
    if ($app instanceof Application) {
158✔
492
        return $app;
158✔
493
    }
494

495
    if (\is_null($caller)) {
1✔
496
        $caller = transform(debug_backtrace()[1] ?? null, function ($debug) {
1✔
497
            if (isset($debug['class']) && isset($debug['function'])) {
1✔
498
                return \sprintf('%s::%s', $debug['class'], $debug['function']);
1✔
499
            }
500

501
            return $debug['function'];
×
502
        });
1✔
503
    }
504

505
    throw Exceptions\ApplicationNotAvailableException::make($caller);
1✔
506
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc