LocaleLookupTest.php

Same filename in other branches
  1. 9 core/modules/locale/tests/src/Unit/LocaleLookupTest.php
  2. 8.9.x core/modules/locale/tests/src/Unit/LocaleLookupTest.php
  3. 10 core/modules/locale/tests/src/Unit/LocaleLookupTest.php

Namespace

Drupal\Tests\locale\Unit

File

core/modules/locale/tests/src/Unit/LocaleLookupTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\locale\Unit;

use Drupal\Component\Gettext\PoItem;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\locale\LocaleLookup;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * @coversDefaultClass \Drupal\locale\LocaleLookup
 * @group locale
 */
class LocaleLookupTest extends UnitTestCase {
    
    /**
     * A mocked storage to use when instantiating LocaleTranslation objects.
     *
     * @var \Drupal\locale\StringStorageInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $storage;
    
    /**
     * A mocked cache object.
     *
     * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $cache;
    
    /**
     * A mocked lock object.
     *
     * @var \Drupal\Core\Lock\LockBackendInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $lock;
    
    /**
     * A mocked user object built from AccountInterface.
     *
     * @var \Drupal\Core\Session\AccountInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $user;
    
    /**
     * A mocked config factory built with UnitTestCase::getConfigFactoryStub().
     *
     * @var \Drupal\Core\Config\ConfigFactory|\PHPUnit\Framework\MockObject\MockBuilder
     */
    protected $configFactory;
    
    /**
     * A mocked language manager built from LanguageManagerInterface.
     *
     * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $languageManager;
    
    /**
     * The request stack.
     *
     * @var \Symfony\Component\HttpFoundation\RequestStack
     */
    protected $requestStack;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->storage = $this->createMock('Drupal\\locale\\StringStorageInterface');
        $this->cache = $this->createMock('Drupal\\Core\\Cache\\CacheBackendInterface');
        $this->lock = $this->createMock('Drupal\\Core\\Lock\\LockBackendInterface');
        $this->lock
            ->expects($this->never())
            ->method($this->anything());
        $this->user = $this->createMock('Drupal\\Core\\Session\\AccountInterface');
        $this->user
            ->expects($this->any())
            ->method('getRoles')
            ->willReturn([
            'anonymous',
        ]);
        $this->configFactory = $this->getConfigFactoryStub([
            'locale.settings' => [
                'cache_strings' => FALSE,
            ],
        ]);
        $this->languageManager = $this->createMock('Drupal\\Core\\Language\\LanguageManagerInterface');
        $this->requestStack = new RequestStack();
        $container = new ContainerBuilder();
        $container->set('current_user', $this->user);
        \Drupal::setContainer($container);
    }
    
    /**
     * Tests locale lookups without fallback.
     *
     * @covers ::resolveCacheMiss
     */
    public function testResolveCacheMissWithoutFallback() : void {
        $args = [
            'language' => 'en',
            'source' => 'test',
            'context' => 'irrelevant',
        ];
        $result = (object) [
            'translation' => 'test',
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with('locale:en:irrelevant:anonymous', FALSE);
        $this->storage
            ->expects($this->once())
            ->method('findTranslation')
            ->with($this->equalTo($args))
            ->willReturn($result);
        $locale_lookup = $this->getMockBuilder('Drupal\\locale\\LocaleLookup')
            ->setConstructorArgs([
            'en',
            'irrelevant',
            $this->storage,
            $this->cache,
            $this->lock,
            $this->configFactory,
            $this->languageManager,
            $this->requestStack,
        ])
            ->onlyMethods([
            'persist',
        ])
            ->getMock();
        $locale_lookup->expects($this->never())
            ->method('persist');
        $this->assertSame('test', $locale_lookup->get('test'));
    }
    
    /**
     * Tests locale lookups with fallback.
     *
     * Note that context is irrelevant here. It is not used but it is required.
     *
     * @covers ::resolveCacheMiss
     *
     * @dataProvider resolveCacheMissWithFallbackProvider
     */
    public function testResolveCacheMissWithFallback($langcode, $string, $context, $expected) : void {
        // These are fake words!
        // cSpell:disable
        $translations = [
            'en' => [
                'test' => 'test',
                'fake' => 'fake',
                'missing pl' => 'missing pl',
                'missing cs' => 'missing cs',
                'missing both' => 'missing both',
            ],
            'pl' => [
                'test' => 'test po polsku',
                'fake' => 'ściema',
                'missing cs' => 'zaginiony czech',
            ],
            'cs' => [
                'test' => 'test v české',
                'fake' => 'falešný',
                'missing pl' => 'chybějící pl',
            ],
        ];
        // cSpell:enable
        $this->storage
            ->expects($this->any())
            ->method('findTranslation')
            ->willReturnCallback(function ($argument) use ($translations) {
            if (isset($translations[$argument['language']][$argument['source']])) {
                return (object) [
                    'translation' => $translations[$argument['language']][$argument['source']],
                ];
            }
            return TRUE;
        });
        $this->languageManager
            ->expects($this->any())
            ->method('getFallbackCandidates')
            ->willReturnCallback(function (array $context = []) {
            switch ($context['langcode']) {
                case 'pl':
                    return [
                        'cs',
                        'en',
                    ];
                case 'cs':
                    return [
                        'en',
                    ];
                default:
                    return [];
            }
        });
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with('locale:' . $langcode . ':' . $context . ':anonymous', FALSE);
        $locale_lookup = new LocaleLookup($langcode, $context, $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
        $this->assertSame($expected, $locale_lookup->get($string));
    }
    
    /**
     * Provides test data for testResolveCacheMissWithFallback().
     */
    public static function resolveCacheMissWithFallbackProvider() {
        // cSpell:disable
        return [
            [
                'cs',
                'test',
                'irrelevant',
                'test v české',
            ],
            [
                'cs',
                'fake',
                'irrelevant',
                'falešný',
            ],
            [
                'cs',
                'missing pl',
                'irrelevant',
                'chybějící pl',
            ],
            [
                'cs',
                'missing cs',
                'irrelevant',
                'missing cs',
            ],
            [
                'cs',
                'missing both',
                'irrelevant',
                'missing both',
            ],
            // Testing PL with fallback to cs, en.
[
                'pl',
                'test',
                'irrelevant',
                'test po polsku',
            ],
            [
                'pl',
                'fake',
                'irrelevant',
                'ściema',
            ],
            [
                'pl',
                'missing pl',
                'irrelevant',
                'chybějící pl',
            ],
            [
                'pl',
                'missing cs',
                'irrelevant',
                'zaginiony czech',
            ],
            [
                'pl',
                'missing both',
                'irrelevant',
                'missing both',
            ],
        ];
        // cSpell:enable
    }
    
    /**
     * Tests locale lookups with persistent tracking.
     *
     * @covers ::resolveCacheMiss
     */
    public function testResolveCacheMissWithPersist() : void {
        $args = [
            'language' => 'en',
            'source' => 'test',
            'context' => 'irrelevant',
        ];
        $result = (object) [
            'translation' => 'test',
        ];
        $this->storage
            ->expects($this->once())
            ->method('findTranslation')
            ->with($this->equalTo($args))
            ->willReturn($result);
        $this->configFactory = $this->getConfigFactoryStub([
            'locale.settings' => [
                'cache_strings' => TRUE,
            ],
        ]);
        $locale_lookup = $this->getMockBuilder('Drupal\\locale\\LocaleLookup')
            ->setConstructorArgs([
            'en',
            'irrelevant',
            $this->storage,
            $this->cache,
            $this->lock,
            $this->configFactory,
            $this->languageManager,
            $this->requestStack,
        ])
            ->onlyMethods([
            'persist',
        ])
            ->getMock();
        $locale_lookup->expects($this->once())
            ->method('persist');
        $this->assertSame('test', $locale_lookup->get('test'));
    }
    
    /**
     * Tests locale lookups without a found translation.
     *
     * @covers ::resolveCacheMiss
     */
    public function testResolveCacheMissNoTranslation() : void {
        $string = $this->createMock('Drupal\\locale\\StringInterface');
        $string->expects($this->once())
            ->method('addLocation')
            ->willReturnSelf();
        $this->storage
            ->expects($this->once())
            ->method('findTranslation')
            ->willReturn(NULL);
        $this->storage
            ->expects($this->once())
            ->method('createString')
            ->willReturn($string);
        $request = Request::create('/test');
        $this->requestStack
            ->push($request);
        $locale_lookup = $this->getMockBuilder('Drupal\\locale\\LocaleLookup')
            ->setConstructorArgs([
            'en',
            'irrelevant',
            $this->storage,
            $this->cache,
            $this->lock,
            $this->configFactory,
            $this->languageManager,
            $this->requestStack,
        ])
            ->onlyMethods([
            'persist',
        ])
            ->getMock();
        $locale_lookup->expects($this->never())
            ->method('persist');
        $this->assertTrue($locale_lookup->get('test'));
    }
    
    /**
     * Tests locale lookups with old plural style of translations.
     *
     * @param array $translations
     *   The source with translations.
     * @param string $langcode
     *   The language code of translation string.
     * @param string $string
     *   The string for translation.
     * @param bool $is_fix
     *   The flag about expected fix translation.
     *
     * @covers ::resolveCacheMiss
     * @dataProvider providerFixOldPluralTranslationProvider
     */
    public function testFixOldPluralStyleTranslations($translations, $langcode, $string, $is_fix) : void {
        $this->storage
            ->expects($this->any())
            ->method('findTranslation')
            ->willReturnCallback(function ($argument) use ($translations) {
            if (isset($translations[$argument['language']][$argument['source']])) {
                return (object) [
                    'translation' => $translations[$argument['language']][$argument['source']],
                ];
            }
            return TRUE;
        });
        $this->languageManager
            ->expects($this->any())
            ->method('getFallbackCandidates')
            ->willReturnCallback(function (array $context = []) {
            switch ($context['langcode']) {
                case 'by':
                    return [
                        'ru',
                    ];
            }
        });
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with('locale:' . $langcode . '::anonymous', FALSE);
        $locale_lookup = new LocaleLookup($langcode, '', $this->storage, $this->cache, $this->lock, $this->configFactory, $this->languageManager, $this->requestStack);
        if ($is_fix) {
            $this->assertStringNotContainsString('@count[2]', $locale_lookup->get($string));
        }
        else {
            $this->assertStringContainsString('@count[2]', $locale_lookup->get($string));
        }
    }
    
    /**
     * Provides test data for testResolveCacheMissWithFallback().
     */
    public static function providerFixOldPluralTranslationProvider() {
        $translations = [
            'by' => [
                'word1' => '@count[2] word-by',
                'word2' => implode(PoItem::DELIMITER, [
                    'word-by',
                    '@count[2] word-by',
                ]),
            ],
            'ru' => [
                'word3' => '@count[2] word-ru',
                'word4' => implode(PoItem::DELIMITER, [
                    'word-ru',
                    '@count[2] word-ru',
                ]),
            ],
        ];
        return [
            'no-plural' => [
                $translations,
                'by',
                'word1',
                FALSE,
            ],
            'no-plural from other language' => [
                $translations,
                'by',
                'word3',
                FALSE,
            ],
            'plural' => [
                $translations,
                'by',
                'word2',
                TRUE,
            ],
            'plural from other language' => [
                $translations,
                'by',
                'word4',
                TRUE,
            ],
        ];
    }
    
    /**
     * @covers ::getCid
     *
     * @dataProvider getCidProvider
     */
    public function testGetCid(array $roles, $expected) : void {
        $this->user = $this->createMock('Drupal\\Core\\Session\\AccountInterface');
        $this->user
            ->expects($this->any())
            ->method('getRoles')
            ->willReturn($roles);
        $container = new ContainerBuilder();
        $container->set('current_user', $this->user);
        \Drupal::setContainer($container);
        $locale_lookup = $this->getMockBuilder('Drupal\\locale\\LocaleLookup')
            ->setConstructorArgs([
            'en',
            'irrelevant',
            $this->storage,
            $this->cache,
            $this->lock,
            $this->configFactory,
            $this->languageManager,
            $this->requestStack,
        ])
            ->getMock();
        $o = new \ReflectionObject($locale_lookup);
        $method = $o->getMethod('getCid');
        $cid = $method->invoke($locale_lookup, 'getCid');
        $this->assertEquals($expected, $cid);
    }
    
    /**
     * Provides test data for testGetCid().
     */
    public static function getCidProvider() {
        return [
            [
                [
                    'a',
                ],
                'locale:en:irrelevant:a',
            ],
            [
                [
                    'a',
                    'b',
                ],
                'locale:en:irrelevant:a:b',
            ],
            [
                [
                    'b',
                    'a',
                ],
                'locale:en:irrelevant:a:b',
            ],
        ];
    }

}

Classes

Title Deprecated Summary
LocaleLookupTest @coversDefaultClass \Drupal\locale\LocaleLookup @group locale

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.