Current File : /home/jvzmxxx/wiki1/extensions/Flow/tests/phpunit/LinksTableTest.php
<?php

namespace Flow\Tests;

use Flow\Container;
use Flow\Data\ManagerGroup;
use Flow\Data\Listener\ReferenceRecorder;
use Flow\Exception\WikitextException;
use Flow\LinksTableUpdater;
use Flow\Model\AbstractRevision;
use Flow\Model\PostRevision;
use Flow\Model\UUID;
use Flow\Model\Workflow;
use Flow\Parsoid\ReferenceExtractor;
use Flow\Parsoid\ReferenceFactory;
use Flow\Conversion\Utils;
use ParserOutput;
use Title;

/**
 * @group Flow
 * @group Database
 */
class LinksTableTest extends PostRevisionTestCase {
	/**
	 * @var array
	 */
	protected $tablesUsed = array(
		'flow_ext_ref',
		'flow_revision',
		'flow_topic_list',
		'flow_tree_node',
		'flow_tree_revision',
		'flow_wiki_ref',
		'flow_workflow',
		'page',
		'revision',
		'text',
	);

	/**
	 * @var ManagerGroup
	 */
	protected $storage;

	/**
	 * @var ReferenceExtractor
	 */
	protected $extractor;

	/**
	 * @var ReferenceRecorder
	 */
	protected $recorder;

	/**
	 * @var LinksTableUpdater
	 */
	protected $updater;

	/**
	 * @var Workflow
	 */
	protected $workflow;

	/**
	 * @var PostRevision
	 */
	protected $revision;

	protected function setUp() {
		parent::setUp();

		// create a workflow & revision associated with it
		$this->revision = $this->generateObject();
		$this->workflow = $this->workflows[$this->revision->getCollectionId()->getAlphadecimal()];
		$this->storage = Container::get( 'storage' );
		$this->extractor = Container::get( 'reference.extractor' );
		$this->recorder = Container::get( 'reference.recorder' );
		$this->updater = Container::get( 'reference.updater.links-tables' );

		// Check for Parsoid
		try {
			Utils::convert( 'html', 'wikitext', 'Foo', $this->workflow->getOwnerTitle() );
		} catch ( WikitextException $excep ) {
			$this->markTestSkipped( 'Parsoid not enabled' );
		}

		// These tests don't provide sufficient data to properly run all listeners
		$this->clearExtraLifecycleHandlers();
	}

	/**
	 * Generate a reply to $this->revision (which is a topic title)
	 *
	 * @param array $overrides
	 * @return PostRevision
	 * @throws \Flow\Exception\FlowException
	 */
	protected function generatePost( $overrides ) {
		$uuid = UUID::create();
		return $this->generateObject( $overrides + array(
			'rev_change_type' => 'reply',

			// generate new post id
			'tree_rev_descendant_id' => $uuid->getBinary(),
			'rev_type_id' => $uuid->getBinary(),

			// make sure it's a reply to $this->revision
			'tree_parent_id' => $this->revision->getPostId(),
		) );
	}

	protected static function getTestTitle() {
		return Title::newFromText( 'UTPage' );
	}

	public static function provideGetReferencesFromRevisionContent() {
		return array(
			array(
				'[[Foo]]',
				array(
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'link',
						'value' => 'Foo',
					),
				),
			),
			array(
				'[http://www.google.com Foo]',
				array(
					array(
						'factoryMethod' => 'createUrlReference',
						'refType' => 'link',
						'value' => 'http://www.google.com',
					),
				),
			),
			array(
				'[[File:Foo.jpg]]',
				array(
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'file',
						'value' => 'File:Foo.jpg',
					),
				),
			),
			array(
				'{{Foo}}',
				array(
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'template',
						'value' => 'Template:Foo',
					),
				),
			),
			array(
				'{{Foo}} [[Foo]] [[File:Foo.jpg]] {{Foo}} [[Bar]]',
				array(
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'template',
						'value' => 'Template:Foo',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'link',
						'value' => 'Foo',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'file',
						'value' => 'File:Foo.jpg',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'link',
						'value' => 'Bar',
					),
				),
			)
		);
	}

	/**
	 * @dataProvider provideGetReferencesFromRevisionContent
	 */
	public function testGetReferencesFromRevisionContent( $content, $expectedReferences ) {
		$content = Utils::convert( 'wikitext', 'html', $content, $this->workflow->getOwnerTitle() );
		$revision = $this->generatePost( array( 'rev_content' => $content ) );

		$expectedReferences = $this->expandReferences( $this->workflow, $revision, $expectedReferences );

		$foundReferences = $this->recorder->getReferencesFromRevisionContent( $this->workflow, $revision );

		$this->assertReferenceListsEqual( $expectedReferences, $foundReferences );
	}

	/**
	 * @dataProvider provideGetReferencesFromRevisionContent
	 */
	public function testGetReferencesAfterRevisionInsert( $content, $expectedReferences ) {
		$content = Utils::convert( 'wikitext', 'html', $content, $this->workflow->getOwnerTitle() );
		$revision = $this->generatePost( array( 'rev_content' => $content ) );

		// Save to storage to test if ReferenceRecorder listener picks this up
		$this->store( $this->revision );
		$this->store( $revision );

		$expectedReferences = $this->expandReferences( $this->workflow, $revision, $expectedReferences );

		// References will be stored as linked from Topic:<id>
		$title = Title::newFromText( $this->workflow->getId()->getAlphadecimal(), NS_TOPIC );

		// Retrieve references from storage
		$foundReferences = $this->updater->getReferencesForTitle( $title );

		$this->assertReferenceListsEqual( $expectedReferences, $foundReferences );
	}

	public static function provideGetExistingReferences() {
		return array( /* list of test runs */
			array( /* list of arguments */
				array( /* list of references */
					array( /* list of parameters */
						'factoryMethod' => 'createWikiReference',
						'refType' => 'template',
						'value' => 'Template:Foo',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'link',
						'value' => 'Foo',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'file',
						'value' => 'File:Foo.jpg',
					),
					array(
						'factoryMethod' => 'createWikiReference',
						'refType' => 'link',
						'value' => 'Bar',
					),
				),
			),
		);
	}

	/**
	 * @dataProvider provideGetExistingReferences
	 */
	public function testGetExistingReferences( array $references ) {
		list( $workflow, $revision, $title ) = $this->getBlandTestObjects();

		$references = $this->expandReferences( $workflow, $revision, $references );

		$this->storage->multiPut( $references );

		$foundReferences = $this->recorder
			->getExistingReferences( $revision->getRevisionType(), $revision->getCollectionId() );

		$this->assertReferenceListsEqual( $references, $foundReferences );
	}

	public static function provideReferenceDiff() {
		$references = self::getSampleReferences();

		return array(
			// Just adding a few
			array(
				array(),
				array(
					$references['fooLink'],
					$references['barLink']
				),
				array(
					$references['fooLink'],
					$references['barLink'],
				),
				array(),
			),
			// Removing one
			array(
				array(
					$references['fooLink'],
					$references['barLink']
				),
				array(
					$references['fooLink'],
				),
				array(
				),
				array(
					$references['barLink'],
				),
			),
			// Equality robustness
			array(
				array(
					$references['fooLink'],
				),
				array(
					$references['FooLink'],
				),
				array(
				),
				array(
				),
				array( // test is only valid if Foo and foo are same page
					'wgCapitalLinks' => true,
				)
			),
			// Inequality robustness
			array(
				array(
					$references['fooLink'],
				),
				array(
					$references['barLink'],
				),
				array(
					$references['barLink'],
				),
				array(
					$references['fooLink'],
				),
			),
		);
	}

	/**
	 * @dataProvider provideReferenceDiff
	 */
	public function testReferenceDiff( $old, $new, $expectedAdded, $expectedRemoved, $globals = array() ) {
		if ( $globals ) {
			$this->setMwGlobals( $globals );
		}
		list( $workflow, $revision, $title ) = $this->getBlandTestObjects();

		foreach( array( 'old', 'new', 'expectedAdded', 'expectedRemoved' ) as $varName ) {
			$$varName = $this->expandReferences( $workflow, $revision, $$varName );
		}

		list( $added, $removed ) = $this->recorder->referencesDifference( $old, $new );

		$this->assertReferenceListsEqual( $added, $expectedAdded );
		$this->assertReferenceListsEqual( $removed, $expectedRemoved );
	}

	public static function provideMutateParserOutput() {
		$references = self::getSampleReferences();

		return array(
			array(
				array( // references
					$references['fooLink'],
					$references['fooTemplate'],
					$references['googleLink'],
					$references['fooImage'],
				),
				array(
					'getLinks' => array(
						NS_MAIN => array( 'Foo' => 0, ),
					),
					'getTemplates' => array(
						NS_TEMPLATE => array( 'Foo' => 0, ),
					),
					'getImages' => array(
						'Foo.jpg' => true,
					),
					'getExternalLinks' => array(
						'http://www.google.com' => true,
					),
				),
			),
			array(
				array(
					$references['subpageLink'],
				),
				array(
					'getLinks' => array(
						// NS_MAIN is the namespace of static::getTestTitle()
						NS_MAIN => array( static::getTestTitle()->getDBkey() . '/Subpage' => 0, )
					),
				),
			),
		);
	}

	/**
	 * @dataProvider provideMutateParserOutput
	 */
	public function testMutateParserOutput( $references, $expectedItems ) {
		list( $workflow, $revision, $title ) = $this->getBlandTestObjects();

		/*
		 * Because the data provider is static, we can't access $this->workflow
		 * in there. Once of the things being tested is a subpage link.
		 * Thus, we would have to provide the correct namespace & title for
		 * $this->workflow->getArticleTitle(), under which the subpage will be
		 * created.
		 * Let's work around this by overwriting $workflow->title to a "known"
		 * value, so that we can hardcode that into the expected return value in
		 * the static provider.
		 */
		$title = static::getTestTitle();
		$reflectionWorkflow = new \ReflectionObject( $workflow );
		$reflectionProperty = $reflectionWorkflow->getProperty( 'title' );
		$reflectionProperty->setAccessible( true );
		$reflectionProperty->setValue( $workflow, $title );

		$references = $this->expandReferences( $workflow, $revision, $references );
		$parserOutput = new \ParserOutput;

		// Clear the LinksUpdate to allow clean testing
		foreach( array_keys( $expectedItems ) as $fieldName ) {
			$parserOutput->$fieldName = array();
		}

		$this->updater->mutateParserOutput( $title, $parserOutput, $references );

		foreach( $expectedItems as $method => $content ) {
			$this->assertEquals( $content, $parserOutput->$method(), $method );
		}
	}

	protected function getBlandTestObjects() {
		return array(
			/* workflow = */ $this->workflow,
			/* revision = */ $this->revision,
			/* title = */ $this->workflow->getArticleTitle(),
		);
	}

	protected function expandReferences( Workflow $workflow, AbstractRevision $revision, array $references ) {
		$referenceObjs = array();
		$factory = new ReferenceFactory( $workflow, $revision->getRevisionType(), $revision->getCollectionId() );

		foreach( $references as $ref ) {
			$referenceObjs[] = $factory->{$ref['factoryMethod']}( $ref['refType'], $ref['value'] );
		}

		return $referenceObjs;
	}

	protected static function getSampleReferences() {
		return array(
			'fooLink' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'link',
				'value' => 'Foo',
			),
			'subpageLink' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'link',
				'value' => '/Subpage',
			),
			'FooLink' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'link',
				'value' => 'foo',
			),
			'barLink' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'link',
				'value' => 'Bar',
			),
			'fooTemplate' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'template',
				'value' => 'Template:Foo',
			),
			'googleLink' => array(
				'factoryMethod' => 'createUrlReference',
				'refType' => 'link',
				'value' => 'http://www.google.com'
			),
			'fooImage' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'file',
				'value' => 'File:Foo.jpg',
			),
			'foreignFoo' => array(
				'factoryMethod' => 'createWikiReference',
				'refType' => 'link',
				'value' => 'Foo',
			),
		);
	}

	protected function flattenReferenceList( $input ) {
		$list = array();

		foreach( $input as $reference ) {
			$list[$reference->getUniqueIdentifier()] = $reference;
		}

		ksort( $list );
		return array_keys( $list );
	}

	protected function assertReferenceListsEqual( $input1, $input2 ) {
		$list1 = $this->flattenReferenceList( $input1 );
		$list2 = $this->flattenReferenceList( $input2 );

		$this->assertEquals( $list1, $list2 );
	}
}