You are here

class StemmerTest in Search API 8

Tests the "Stemmer" processor.

@coversDefaultClass \Drupal\search_api\Plugin\search_api\processor\Stemmer

@group search_api

Hierarchy

Expanded class hierarchy of StemmerTest

File

tests/src/Unit/Processor/StemmerTest.php, line 24

Namespace

Drupal\Tests\search_api\Unit\Processor
View source
class StemmerTest extends UnitTestCase {
  use ProcessorTestTrait;
  use TestItemsTrait;

  /**
   * Creates a new processor object for use in the tests.
   */
  protected function setUp() {
    parent::setUp();
    $this
      ->setUpMockContainer();
    $this->processor = new Stemmer([], 'string', []);
  }

  /**
   * Tests the supportsIndex() method.
   *
   * @covers ::supportsIndex
   */
  public function testSupportsIndex() {
    $index = $this
      ->createMock(IndexInterface::class);
    $language_manager = $this
      ->createMock(LanguageManagerInterface::class);
    $this->container
      ->set('language_manager', $language_manager);
    $language_manager
      ->method('getLanguages')
      ->willReturn([
      new Language([
        'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ]),
    ]);
    $this
      ->assertFalse(Stemmer::supportsIndex($index));
    $language_manager = $this
      ->createMock(LanguageManagerInterface::class);
    $this->container
      ->set('language_manager', $language_manager);
    $language_manager
      ->method('getLanguages')
      ->willReturn([
      new Language([
        'id' => 'de',
      ]),
      new Language([
        'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ]),
      new Language([
        'id' => 'fr',
      ]),
    ]);
    $this
      ->assertFalse(Stemmer::supportsIndex($index));
    $language_manager = $this
      ->createMock(LanguageManagerInterface::class);
    $this->container
      ->set('language_manager', $language_manager);
    $language_manager
      ->method('getLanguages')
      ->willReturn([
      new Language([
        'id' => 'en',
      ]),
      new Language([
        'id' => 'fr',
      ]),
      new Language([
        'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ]),
    ]);
    $this
      ->assertTrue(Stemmer::supportsIndex($index));
    $language_manager = $this
      ->createMock(LanguageManagerInterface::class);
    $this->container
      ->set('language_manager', $language_manager);
    $language_manager
      ->method('getLanguages')
      ->willReturn([
      new Language([
        'id' => 'fr',
      ]),
      new Language([
        'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
      ]),
      new Language([
        'id' => 'en-GB',
      ]),
    ]);
    $this
      ->assertTrue(Stemmer::supportsIndex($index));
  }

  /**
   * Tests the preprocessIndexItems() method.
   *
   * @covers ::preprocessIndexItems
   */
  public function testPreprocessIndexItems() {
    $index = $this
      ->createMock(IndexInterface::class);
    $item_en = $this
      ->getMockBuilder(ItemInterface::class)
      ->disableOriginalConstructor()
      ->getMock();
    $item_en
      ->method('getLanguage')
      ->willReturn('en');
    $field_en = new Field($index, 'foo');
    $field_en
      ->setType('text');
    $field_en
      ->setValues([
      new TextValue('ties'),
    ]);
    $item_en
      ->method('getFields')
      ->willReturn([
      'foo' => $field_en,
    ]);
    $item_de = $this
      ->getMockBuilder(ItemInterface::class)
      ->disableOriginalConstructor()
      ->getMock();
    $item_de
      ->method('getLanguage')
      ->willReturn('de');
    $field_de = new Field($index, 'foo');
    $field_de
      ->setType('text');
    $field_de
      ->setValues([
      new TextValue('ties'),
    ]);
    $item_de
      ->method('getFields')
      ->willReturn([
      'foo' => $field_de,
    ]);
    $items = [
      $item_en,
      $item_de,
    ];
    $this->processor
      ->preprocessIndexItems($items);

    /** @var \Drupal\search_api\Plugin\search_api\data_type\value\TextValueInterface $value */
    $value = $field_en
      ->getValues()[0];
    $this
      ->assertEquals('tie', $value
      ->toText());
    $value = $field_de
      ->getValues()[0];
    $this
      ->assertEquals('ties', $value
      ->toText());
  }

  /**
   * Tests the preprocessSearchQuery() method.
   *
   * @param string[]|null $languages
   *   The languages to set for the query.
   * @param bool $should_process
   *   Whether keys are expected to be processed by the processor or not.
   *
   * @covers ::preprocessSearchQuery
   *
   * @dataProvider preprocessSearchQueryDataProvider
   */
  public function testPreprocessSearchQuery(array $languages = NULL, $should_process) {

    /** @var \Drupal\search_api\Query\QueryInterface|\PHPUnit\Framework\MockObject\MockObject $query */
    $query = $this
      ->createMock(QueryInterface::class);
    $query
      ->method('getLanguages')
      ->willReturn($languages);

    // Unfortunately, returning a reference (as getKeys() has to do for
    // processing to work) doesn't seem to be possible with a mock object. But
    // since the only code we really want to test is the language check, using
    // an exception works just as well, and is quite simple.
    $query
      ->method('getKeys')
      ->willThrowException(new \RuntimeException());
    try {
      $this->processor
        ->preprocessSearchQuery($query);
      $this
        ->assertFalse($should_process, "Keys weren't processed but should have been.");
    } catch (\RuntimeException $e) {
      $this
        ->assertTrue($should_process, "Keys were processed but shouldn't have been.");
    }
  }

  /**
   * Provides sets of arguments for testPreprocessSearchQuery().
   *
   * @return array[]
   *   Arrays of arguments for testPreprocessSearchQuery().
   */
  public function preprocessSearchQueryDataProvider() {
    return [
      'language-less query' => [
        NULL,
        TRUE,
      ],
      'English query' => [
        [
          'en',
        ],
        TRUE,
      ],
      'Non-English query' => [
        [
          'de',
        ],
        FALSE,
      ],
      'Multilingual query (including English)' => [
        [
          'en',
          'fr',
          'es',
        ],
        TRUE,
      ],
      'Multilingual query (not including English)' => [
        [
          'de',
          'it',
        ],
        FALSE,
      ],
    ];
  }

  /**
   * Tests the process() method.
   *
   * @param string $passed_value
   *   The value that should be passed into process().
   * @param string $expected_value
   *   The expected processed value.
   *
   * @covers ::process
   *
   * @dataProvider processDataProvider
   */
  public function testProcess($passed_value, $expected_value) {
    $this
      ->invokeMethod('process', [
      &$passed_value,
    ]);
    $this
      ->assertEquals($passed_value, $expected_value);
  }

  /**
   * Provides sets of arguments for testProcess().
   *
   * @return array[]
   *   Arrays of arguments for testProcess().
   */
  public function processDataProvider() {
    return [
      [
        'Yo',
        'yo',
      ],
      [
        'ties',
        'tie',
      ],
      [
        'cries',
        'cri',
      ],
      [
        'exceed',
        'exceed',
      ],
      [
        'consign',
        'consign',
      ],
      [
        'consigned',
        'consign',
      ],
      [
        'consigning',
        'consign',
      ],
      [
        'consignment',
        'consign',
      ],
      [
        'consist',
        'consist',
      ],
      [
        'consisted',
        'consist',
      ],
      [
        'consistency',
        'consist',
      ],
      [
        'consistent',
        'consist',
      ],
      [
        'consistently',
        'consist',
      ],
      [
        'consisting',
        'consist',
      ],
      [
        'consists',
        'consist',
      ],
      [
        'consolation',
        'consol',
      ],
      [
        'consolations',
        'consol',
      ],
      [
        'consolatory',
        'consolatori',
      ],
      [
        'console',
        'consol',
      ],
      [
        'consoled',
        'consol',
      ],
      [
        'consoles',
        'consol',
      ],
      [
        'consolidate',
        'consolid',
      ],
      [
        'consolidated',
        'consolid',
      ],
      [
        'consolidating',
        'consolid',
      ],
      [
        'consoling',
        'consol',
      ],
      [
        'consolingly',
        'consol',
      ],
      [
        'consols',
        'consol',
      ],
      [
        'consonant',
        'conson',
      ],
      [
        'consort',
        'consort',
      ],
      [
        'consorted',
        'consort',
      ],
      [
        'consorting',
        'consort',
      ],
      [
        'conspicuous',
        'conspicu',
      ],
      [
        'conspicuously',
        'conspicu',
      ],
      [
        'conspiracy',
        'conspiraci',
      ],
      [
        'conspirator',
        'conspir',
      ],
      [
        'conspirators',
        'conspir',
      ],
      [
        'conspire',
        'conspir',
      ],
      [
        'conspired',
        'conspir',
      ],
      [
        'conspiring',
        'conspir',
      ],
      [
        'constable',
        'constabl',
      ],
      [
        'constables',
        'constabl',
      ],
      [
        'constance',
        'constanc',
      ],
      [
        'constancy',
        'constanc',
      ],
      [
        'constant',
        'constant',
      ],
      [
        'knack',
        'knack',
      ],
      [
        'knackeries',
        'knackeri',
      ],
      [
        'knacks',
        'knack',
      ],
      [
        'knag',
        'knag',
      ],
      [
        'knave',
        'knave',
      ],
      [
        'knaves',
        'knave',
      ],
      [
        'knavish',
        'knavish',
      ],
      [
        'kneaded',
        'knead',
      ],
      [
        'kneading',
        'knead',
      ],
      [
        'knee',
        'knee',
      ],
      [
        'kneel',
        'kneel',
      ],
      [
        'kneeled',
        'kneel',
      ],
      [
        'kneeling',
        'kneel',
      ],
      [
        'kneels',
        'kneel',
      ],
      [
        'knees',
        'knee',
      ],
      [
        'knell',
        'knell',
      ],
      [
        'knelt',
        'knelt',
      ],
      [
        'knew',
        'knew',
      ],
      [
        'knick',
        'knick',
      ],
      [
        'knif',
        'knif',
      ],
      [
        'knife',
        'knife',
      ],
      [
        'knight',
        'knight',
      ],
      [
        'knightly',
        'knight',
      ],
      [
        'knights',
        'knight',
      ],
      [
        'knit',
        'knit',
      ],
      [
        'knits',
        'knit',
      ],
      [
        'knitted',
        'knit',
      ],
      [
        'knitting',
        'knit',
      ],
      [
        'knives',
        'knive',
      ],
      [
        'knob',
        'knob',
      ],
      [
        'knobs',
        'knob',
      ],
      [
        'knock',
        'knock',
      ],
      [
        'knocked',
        'knock',
      ],
      [
        'knocker',
        'knocker',
      ],
      [
        'knockers',
        'knocker',
      ],
      [
        'knocking',
        'knock',
      ],
      [
        'knocks',
        'knock',
      ],
      [
        'knopp',
        'knopp',
      ],
      [
        'knot',
        'knot',
      ],
      [
        'knots',
        'knot',
      ],
      // This can happen when Tokenizer is off during indexing, or when
      // preprocessing a search query with quoted keywords.
      [
        " \tExtra  spaces \rappeared \n",
        'extra space appear',
      ],
      [
        "\tspaced-out  \r\n",
        'space out',
      ],
    ];
  }

  /**
   * Tests whether "IS NULL" conditions are correctly kept.
   *
   * @see https://www.drupal.org/project/search_api/issues/3212925
   */
  public function testIsNullConditions() {
    $index = $this
      ->createMock(IndexInterface::class);
    $index
      ->method('getFields')
      ->willReturn([
      'field' => (new Field($index, 'field'))
        ->setType('string'),
    ]);
    $this->processor
      ->setIndex($index);
    $passed_value = NULL;
    $this
      ->invokeMethod('processConditionValue', [
      &$passed_value,
    ]);
    $this
      ->assertSame(NULL, $passed_value);
    $condition = new Condition('field', NULL);
    $conditions = [
      $condition,
    ];
    $this
      ->invokeMethod('processConditions', [
      &$conditions,
    ]);
    $this
      ->assertSame([
      $condition,
    ], $conditions);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
ProcessorTestTrait::$processor protected property The tested processor.
ProcessorTestTrait::invokeMethod protected function Invokes a method on the processor.
StemmerTest::preprocessSearchQueryDataProvider public function Provides sets of arguments for testPreprocessSearchQuery().
StemmerTest::processDataProvider public function Provides sets of arguments for testProcess().
StemmerTest::setUp protected function Creates a new processor object for use in the tests. Overrides UnitTestCase::setUp
StemmerTest::testIsNullConditions public function Tests whether "IS NULL" conditions are correctly kept.
StemmerTest::testPreprocessIndexItems public function Tests the preprocessIndexItems() method.
StemmerTest::testPreprocessSearchQuery public function Tests the preprocessSearchQuery() method.
StemmerTest::testProcess public function Tests the process() method.
StemmerTest::testSupportsIndex public function Tests the supportsIndex() method.
TestItemsTrait::$container protected property The class container.
TestItemsTrait::$itemIds protected property The used item IDs for test items.
TestItemsTrait::createItems public function Creates a certain number of test items.
TestItemsTrait::createSingleFieldItem public function Creates an array with a single item which has the given field.
TestItemsTrait::setUpMockContainer protected function Adds a container with several mock services commonly needed by our tests.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getBlockMockWithMachineName Deprecated protected function Mocks a block with a block plugin. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.