Skip to content

Commit

Permalink
chore: add test for fix plugin fakeip order
Browse files Browse the repository at this point in the history
  • Loading branch information
ruano-a committed Dec 6, 2022
1 parent f4530ab commit 7ea96c5
Show file tree
Hide file tree
Showing 6 changed files with 381 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"php-http/curl-client": "^2.2",
"php-http/message": "^1.13",
"phpstan/phpstan": "^1.9.2",
"symfony/cache": "^4.4 || ^5.0 || ^6.0",
"symfony/config": "^4.4 || ^5.0 || ^6.0",
"symfony/phpunit-bridge": "^5.2 || ^6.0",
"symfony/validator": "^4.4 || ^5.0 || ^6.0",
Expand Down
247 changes: 247 additions & 0 deletions tests/Functional/CustomTestKernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
<?php

declare(strict_types=1);

/*
* This file is part of the BazingaGeocoderBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Bazinga\GeocoderBundle\Tests\Functional;

use Nyholm\BundleTest\TestKernel;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader;
use Symfony\Component\DependencyInjection\Dumper\Preloader;
use Symfony\Component\ErrorHandler\DebugClassLoader;
use Symfony\Component\Filesystem\Filesystem;

/*
* Needed by PluginInteractionTest, so the test uses the cache for geoCoder, and doesn't clear it each time
* BUT doesn't use the cache for the service parameters, like it is happening in dev
*
* warmupDir is redefined because the method initializeContainer() is using it, but it's private in the parent.
* the methods using it (reboot() and getKernelParameters() and setAnnotatedClassCache() ) therefore needed to be redeclared, in order
* for them to have a correct value in it.
*/
class CustomTestKernel extends TestKernel
{
private $warmupDir;

/**
* {@inheritdoc}
*/
public function reboot(?string $warmupDir)
{
$this->shutdown();
$this->warmupDir = $warmupDir;
$this->boot();
}

/*
* Needed, otherwise the used cache is different on each kernel boot, which is a big issue in PluginInteractionTest
*/
public function getCacheDir(): string
{
return realpath(sys_get_temp_dir()).'/NyholmBundleTest/cachePluginInteractionTest';
}

/**
* Returns the kernel parameters.
*/
protected function getKernelParameters(): array
{
$bundles = [];
$bundlesMetadata = [];

foreach ($this->bundles as $name => $bundle) {
$bundles[$name] = \get_class($bundle);
$bundlesMetadata[$name] = [
'path' => $bundle->getPath(),
'namespace' => $bundle->getNamespace(),
];
}

return [
'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(),
'kernel.environment' => $this->environment,
'kernel.runtime_environment' => '%env(default:kernel.environment:APP_RUNTIME_ENV)%',
'kernel.debug' => $this->debug,
'kernel.build_dir' => realpath($buildDir = $this->warmupDir ?: $this->getBuildDir()) ?: $buildDir,
'kernel.cache_dir' => realpath($cacheDir = ($this->getCacheDir() === $this->getBuildDir() ? ($this->warmupDir ?: $this->getCacheDir()) : $this->getCacheDir())) ?: $cacheDir,
'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(),
'kernel.bundles' => $bundles,
'kernel.bundles_metadata' => $bundlesMetadata,
'kernel.charset' => $this->getCharset(),
'kernel.container_class' => $this->getContainerClass(),
];
}

/**
* @internal
*/
public function setAnnotatedClassCache(array $annotatedClasses)
{
file_put_contents(($this->warmupDir ?: $this->getBuildDir()).'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
}

/**
* Initializes the service container.
*
* The built version of the service container is used when fresh, otherwise the
* container is built.
*/
protected function initializeContainer()
{
$class = $this->getContainerClass();
$buildDir = $this->warmupDir ?: $this->getBuildDir();
$cache = new ConfigCache($buildDir.'/'.$class.'.php', $this->debug);
$cachePath = $cache->getPath();

// Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);

try {
if (false && \is_object($this->container = include $cachePath)
&& (!$this->debug || (self::$freshCache[$cachePath] ?? $cache->isFresh()))
) {
self::$freshCache[$cachePath] = true;
$this->container->set('kernel', $this);
error_reporting($errorLevel);

return;
}
} catch (\Throwable $e) {
}

$oldContainer = \is_object($this->container) ? new \ReflectionClass($this->container) : $this->container = null;

try {
is_dir($buildDir) ?: mkdir($buildDir, 0777, true);

if ($lock = fopen($cachePath.'.lock', 'w')) {
if (!flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock) && !flock($lock, $wouldBlock ? \LOCK_SH : \LOCK_EX)) {
fclose($lock);
$lock = null;
} elseif (true || !\is_object($this->container = include $cachePath)) {
$this->container = null;
} elseif (!$oldContainer || \get_class($this->container) !== $oldContainer->name) {
flock($lock, \LOCK_UN);
fclose($lock);
$this->container->set('kernel', $this);

return;
}
}
} catch (\Throwable $e) {
} finally {
error_reporting($errorLevel);
}

if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
$collectedLogs = [];
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
}

if (isset($collectedLogs[$message])) {
++$collectedLogs[$message]['count'];

return null;
}

$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 5);
// Clean the trace by removing first frames added by the error handler itself.
for ($i = 0; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
$backtrace = \array_slice($backtrace, 1 + $i);
break;
}
}
for ($i = 0; isset($backtrace[$i]); ++$i) {
if (!isset($backtrace[$i]['file'], $backtrace[$i]['line'], $backtrace[$i]['function'])) {
continue;
}
if (!isset($backtrace[$i]['class']) && 'trigger_deprecation' === $backtrace[$i]['function']) {
$file = $backtrace[$i]['file'];
$line = $backtrace[$i]['line'];
$backtrace = \array_slice($backtrace, 1 + $i);
break;
}
}

// Remove frames added by DebugClassLoader.
for ($i = \count($backtrace) - 2; 0 < $i; --$i) {
if (\in_array($backtrace[$i]['class'] ?? null, [DebugClassLoader::class, LegacyDebugClassLoader::class], true)) {
$backtrace = [$backtrace[$i + 1]];
break;
}
}

$collectedLogs[$message] = [
'type' => $type,
'message' => $message,
'file' => $file,
'line' => $line,
'trace' => [$backtrace[0]],
'count' => 1,
];

return null;
});
}

try {
$container = null;
$container = $this->buildContainer();
$container->compile();
} finally {
if ($collectDeprecations) {
restore_error_handler();

@file_put_contents($buildDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
@file_put_contents($buildDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
}
}

$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());

if ($lock) {
flock($lock, \LOCK_UN);
fclose($lock);
}

$this->container = require $cachePath;
$this->container->set('kernel', $this);

if ($oldContainer && \get_class($this->container) !== $oldContainer->name) {
// Because concurrent requests might still be using them,
// old container files are not removed immediately,
// but on a next dump of the container.
static $legacyContainers = [];
$oldContainerDir = \dirname($oldContainer->getFileName());
$legacyContainers[$oldContainerDir.'.legacy'] = true;
foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy', \GLOB_NOSORT) as $legacyContainer) {
if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) {
(new Filesystem())->remove(substr($legacyContainer, 0, -7));
}
}

touch($oldContainerDir.'.legacy');
}

$preload = $this instanceof WarmableInterface ? (array) $this->warmUp($this->container->getParameter('kernel.cache_dir')) : [];

if ($this->container->has('cache_warmer')) {
$preload = array_merge($preload, (array) $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')));
}

if ($preload && method_exists(Preloader::class, 'append') && file_exists($preloadFile = $buildDir.'/'.$class.'.preload.php')) {
Preloader::append($preloadFile, $preload);
}
}
}
76 changes: 76 additions & 0 deletions tests/Functional/PluginInteractionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

/*
* This file is part of the BazingaGeocoderBundle package.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @license MIT License
*/

namespace Bazinga\GeocoderBundle\Tests\Functional;

use Bazinga\GeocoderBundle\BazingaGeocoderBundle;
use Bazinga\GeocoderBundle\Tests\PublicServicePass;
use Geocoder\Query\GeocodeQuery;
use Nyholm\BundleTest\TestKernel;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpKernel\KernelInterface;

final class PluginInteractionTest extends KernelTestCase
{
use ExpectDeprecationTrait;

protected static function getKernelClass(): string
{
return CustomTestKernel::class;
}

protected static function createKernel(array $options = []): KernelInterface
{
/**
* @var TestKernel $kernel
*/
$kernel = parent::createKernel($options);
$kernel->addTestBundle(BazingaGeocoderBundle::class);
$kernel->addTestCompilerPass(new PublicServicePass('|[Bb]azinga:*|'));
$kernel->addTestCompilerPass(new PublicServicePass('|[gG]eocoder:*|'));
$kernel->handleOptions($options);

return $kernel;
}

public function testCachePluginUsesIpFromFakeIpPlugin(): void
{
$kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
$kernel->setClearCacheAfterShutdown(false);
$kernel->addTestConfig(__DIR__.'/config/framework.yml');
$kernel->addTestConfig(__DIR__.'/config/cache_symfony.yml');
$kernel->addTestConfig(__DIR__.'/config/geo_plugin_fakeip_with_cache_cn.yml');
}]);
$kernel->setClearCacheAfterShutdown(false);
$container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();

$geoPluginGeocoder = $container->get('bazinga_geocoder.provider.geoPlugin');
$result = $geoPluginGeocoder->geocodeQuery(GeocodeQuery::create('::1'));
$country = $result->first()->getCountry()->getCode();
self::assertEquals('CN', $country);

$kernel = self::bootKernel(['config' => static function (TestKernel $kernel) {
$kernel->setClearCacheAfterShutdown(false);
$kernel->addTestConfig(__DIR__.'/config/framework.yml');
$kernel->addTestConfig(__DIR__.'/config/cache_symfony.yml');
$kernel->addTestConfig(__DIR__.'/config/geo_plugin_fakeip_with_cache_fr.yml');
}]);
$kernel->setClearCacheAfterShutdown(false);
$container = method_exists(__CLASS__, 'getContainer') ? self::getContainer() : $kernel->getContainer();

$geoPluginGeocoder = $container->get('bazinga_geocoder.provider.geoPlugin');
$result = $geoPluginGeocoder->geocodeQuery(GeocodeQuery::create('::1'));
$country = $result->first()->getCountry()->getCode();
self::assertEquals('FR', $country);
}
}
29 changes: 29 additions & 0 deletions tests/Functional/config/cache_symfony.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
framework:
cache:
# Put the unique name of your app here: the prefix seed
# is used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name

# The app cache caches to the filesystem by default.
# Other options include:

# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost

# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu

# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: ~
app: cache.adapter.filesystem
system: cache.adapter.system
pools:
app.cache.geoPlugin:
adapter: cache.app
default_lifetime: 600
services:
app.simple_cache:
class: Symfony\Component\Cache\Psr16Cache
arguments: ['@app.cache.geoPlugin']
14 changes: 14 additions & 0 deletions tests/Functional/config/geo_plugin_fakeip_with_cache_cn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# See the docs at https://github.com/geocoder-php/BazingaGeocoderBundle
bazinga_geocoder:
# The local IP (127.0.0.1) will be replaced by the fake_ip
# see https://github.com/geocoder-php/BazingaGeocoderBundle/blob/5.0.0/Resources/doc/index.md#fake-local-ip
fake_ip:
local_ip: ::1
ip: 123.123.123.128
# this ip is in china
providers:
geoPlugin:
factory: Bazinga\GeocoderBundle\ProviderFactory\GeoPluginFactory
cache: 'app.simple_cache'
cache_lifetime: 42
cache_precision: ~
14 changes: 14 additions & 0 deletions tests/Functional/config/geo_plugin_fakeip_with_cache_fr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# See the docs at https://github.com/geocoder-php/BazingaGeocoderBundle
bazinga_geocoder:
# The local IP (127.0.0.1) will be replaced by the fake_ip
# see https://github.com/geocoder-php/BazingaGeocoderBundle/blob/5.0.0/Resources/doc/index.md#fake-local-ip
fake_ip:
local_ip: ::1
ip: 87.98.128.10
# this ip is in france
providers:
geoPlugin:
factory: Bazinga\GeocoderBundle\ProviderFactory\GeoPluginFactory
cache: 'app.simple_cache'
cache_lifetime: 42
cache_precision: ~

0 comments on commit 7ea96c5

Please sign in to comment.