diff --git a/src/Statement/Statement.php b/src/Statement/Statement.php index 3fbc08f9..5b2aeaeb 100644 --- a/src/Statement/Statement.php +++ b/src/Statement/Statement.php @@ -25,13 +25,19 @@ class Statement implements Hashable, Comparable, PropertyIdProvider { /** - * Rank enum. Higher values are more preferred. - * - * @since 2.0 + * @deprecated since 4.4, use StatementRank::PREFERRED instead. + */ + const RANK_PREFERRED = StatementRank::PREFERRED; + + /** + * @deprecated since 4.4, use StatementRank::NORMAL instead. */ - const RANK_PREFERRED = 2; - const RANK_NORMAL = 1; - const RANK_DEPRECATED = 0; + const RANK_NORMAL = StatementRank::NORMAL; + + /** + * @deprecated since 4.4, use StatementRank::DEPRECATED instead. + */ + const RANK_DEPRECATED = StatementRank::DEPRECATED; /** * @var string|null @@ -56,9 +62,9 @@ class Statement implements Hashable, Comparable, PropertyIdProvider { private $references; /** - * @var integer, element of the Statement::RANK_ enum + * @var int One of the StatementRank::... constants. */ - private $rank = self::RANK_NORMAL; + private $rank = StatementRank::NORMAL; /** * @since 2.0 @@ -192,19 +198,15 @@ public function addNewReference( $snaks = array() /*...*/ ) { /** * Sets the rank of the statement. - * The rank is an element of the Statement::RANK_ enum. * * @since 0.1 * - * @param integer $rank + * @param int $rank One of the StatementRank::... constants. + * * @throws InvalidArgumentException */ public function setRank( $rank ) { - $ranks = array( self::RANK_DEPRECATED, self::RANK_NORMAL, self::RANK_PREFERRED ); - - if ( !in_array( $rank, $ranks, true ) ) { - throw new InvalidArgumentException( 'Invalid rank specified for statement: ' . var_export( $rank, true ) ); - } + StatementRank::assertIsValid( $rank ); $this->rank = $rank; } diff --git a/src/Statement/StatementRank.php b/src/Statement/StatementRank.php new file mode 100644 index 00000000..a1b21c8c --- /dev/null +++ b/src/Statement/StatementRank.php @@ -0,0 +1,165 @@ + 'deprecated', + self::NORMAL => 'normal', + self::PREFERRED => 'preferred', + ); + + /** + * @return string[] Array mapping all known self::... constants (integers) to string names. + */ + public static function getNames() { + return self::$names; + } + + /** + * @return int[] Array mapping string names to all known self::... constants (integers). + */ + public static function getAllRanks() { + return array_flip( self::$names ); + } + + /** + * @param int $rank + * + * @throws InvalidArgumentException + */ + public static function assertIsValid( $rank ) { + if ( !self::isValid( $rank ) ) { + throw new InvalidArgumentException( 'Invalid rank' ); + } + } + + /** + * @param int $rank + * + * @return bool + */ + public static function isValid( $rank ) { + return is_int( $rank ) && array_key_exists( $rank, self::$names ); + } + + /** + * @param int $rank + * + * @throws InvalidArgumentException + * @return bool Statements with a deprecated (or lower) rank are known to be false. But don't be + * fooled, this does not mean higher ranks are known to be true! + */ + public static function isFalse( $rank ) { + self::assertIsValid( $rank ); + + return $rank === self::DEPRECATED; + } + + /** + * @param int|null $rank1 + * @param int|null $rank2 + * + * @throws InvalidArgumentException + * @return bool True if the given ranks are equal. + */ + public static function isEqual( $rank1, $rank2 ) { + return self::compare( $rank1, $rank2 ) === 0; + } + + /** + * @param int|null $rank1 + * @param int|null $rank2 + * + * @throws InvalidArgumentException + * @return bool True if the first rank is less preferred than the second. + */ + public static function isLower( $rank1, $rank2 ) { + return self::compare( $rank1, $rank2 ) === -1; + } + + /** + * @param int|null $rank1 + * @param int|null $rank2 + * + * @throws InvalidArgumentException + * @return bool True if the first rank is more preferred than the second. + */ + public static function isHigher( $rank1, $rank2 ) { + return self::compare( $rank1, $rank2 ) === 1; + } + + /** + * @param int|null $rank1 + * @param int|null $rank2 + * + * @throws InvalidArgumentException + * @return int 0 if the ranks are equal, -1 if the first rank is less preferred than the second, + * or +1 if the first rank is more preferred than the second. + */ + public static function compare( $rank1, $rank2 ) { + if ( $rank1 !== null ) { + self::assertIsValid( $rank1 ); + } + if ( $rank2 !== null ) { + self::assertIsValid( $rank2 ); + } + + if ( $rank1 === $rank2 ) { + return 0; + } elseif ( $rank1 === null || $rank1 < $rank2 ) { + return -1; + } else { + return 1; + } + } + + /** + * @param int[]|int $ranks + * @param int [$rank2,...] + * + * @return int|null Best rank in the array or list of arguments, or null if none given. + */ + public static function findBestRank( $ranks = array() /*...*/ ) { + if ( !is_array( $ranks ) ) { + $ranks = func_get_args(); + } + + $best = null; + + foreach ( $ranks as $rank ) { + if ( self::isHigher( $rank, $best ) ) { + $best = $rank; + + if ( $best === self::PREFERRED ) { + break; + } + } + } + + return $best; + } + +} diff --git a/tests/unit/Statement/StatementRankTest.php b/tests/unit/Statement/StatementRankTest.php new file mode 100644 index 00000000..1a60af47 --- /dev/null +++ b/tests/unit/Statement/StatementRankTest.php @@ -0,0 +1,320 @@ +assertSame( 0, StatementRank::DEPRECATED ); + $this->assertSame( 1, StatementRank::NORMAL ); + $this->assertSame( 2, StatementRank::PREFERRED ); + } + + public function testGetNames() { + $this->assertSame( array( + 'deprecated', + 'normal', + 'preferred', + ), StatementRank::getNames() ); + } + + public function testGetAllRanks() { + $this->assertSame( array( + 'deprecated' => 0, + 'normal' => 1, + 'preferred' => 2, + ), StatementRank::getAllRanks() ); + } + + /** + * @dataProvider notInIntegerRangeProvider + */ + public function testGivenInvalidRank_assertIsValidThrowsException( $rank ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::assertIsValid( $rank ); + } + + /** + * @dataProvider integerRangeProvider + */ + public function testGivenValidRank_assertIsValidSucceeds( $rank ) { + StatementRank::assertIsValid( $rank ); + $this->assertTrue( true ); + } + + /** + * @dataProvider notInIntegerRangeProvider + */ + public function testGivenInvalidRank_isValidFails( $rank ) { + $this->assertFalse( StatementRank::isValid( $rank ) ); + } + + /** + * @dataProvider integerRangeProvider + */ + public function testGivenInvalidRank_isValidSucceeds( $rank ) { + $this->assertTrue( StatementRank::isValid( $rank ) ); + } + + /** + * @dataProvider notInIntegerRangeProvider + */ + public function testGivenInvalidRank_isFalseThrowsException( $rank ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::isFalse( $rank ); + } + + /** + * @dataProvider isFalseProvider + */ + public function testIsFalse( $rank, $expected ) { + $this->assertSame( $expected, StatementRank::isFalse( $rank ) ); + } + + public function isFalseProvider() { + return array( + array( 0, true ), + array( 1, false ), + array( 2, false ), + ); + } + + /** + * @dataProvider invalidComparisonPairProvider + */ + public function testGivenInvalidRank_isEqualThrowsException( $rank1, $rank2 ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::isEqual( $rank1, $rank2 ); + } + + /** + * @dataProvider isEqualProvider + */ + public function testIsEqual( $rank1, $rank2, $expected ) { + $this->assertSame( $expected, StatementRank::isEqual( $rank1, $rank2 ) ); + } + + public function isEqualProvider() { + return array( + array( null, null, true ), + array( null, 0, false ), + array( null, 1, false ), + array( null, 2, false ), + array( 0, null, false ), + array( 0, 0, true ), + array( 0, 1, false ), + array( 0, 2, false ), + array( 1, null, false ), + array( 1, 0, false ), + array( 1, 1, true ), + array( 1, 2, false ), + array( 2, null, false ), + array( 2, 0, false ), + array( 2, 1, false ), + array( 2, 2, true ), + ); + } + + /** + * @dataProvider invalidComparisonPairProvider + */ + public function testGivenInvalidRank_isLowerThrowsException( $rank1, $rank2 ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::isLower( $rank1, $rank2 ); + } + + /** + * @dataProvider isLowerProvider + */ + public function testIsLower( $rank1, $rank2, $expected ) { + $this->assertSame( $expected, StatementRank::isLower( $rank1, $rank2 ) ); + } + + public function isLowerProvider() { + return array( + array( null, null, false ), + array( null, 0, true ), + array( null, 1, true ), + array( null, 2, true ), + array( 0, null, false ), + array( 0, 0, false ), + array( 0, 1, true ), + array( 0, 2, true ), + array( 1, null, false ), + array( 1, 0, false ), + array( 1, 1, false ), + array( 1, 2, true ), + array( 2, null, false ), + array( 2, 0, false ), + array( 2, 1, false ), + array( 2, 2, false ), + ); + } + + /** + * @dataProvider invalidComparisonPairProvider + */ + public function testGivenInvalidRank_isHigherThrowsException( $rank1, $rank2 ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::isHigher( $rank1, $rank2 ); + } + + /** + * @dataProvider isHigherProvider + */ + public function testIsHigher( $rank1, $rank2, $expected ) { + $this->assertSame( $expected, StatementRank::isHigher( $rank1, $rank2 ) ); + } + + public function isHigherProvider() { + return array( + array( null, null, false ), + array( null, 0, false ), + array( null, 1, false ), + array( null, 2, false ), + array( 0, null, true ), + array( 0, 0, false ), + array( 0, 1, false ), + array( 0, 2, false ), + array( 1, null, true ), + array( 1, 0, true ), + array( 1, 1, false ), + array( 1, 2, false ), + array( 2, null, true ), + array( 2, 0, true ), + array( 2, 1, true ), + array( 2, 2, false ), + ); + } + + /** + * @dataProvider invalidComparisonPairProvider + */ + public function testGivenInvalidRank_compareThrowsException( $rank1, $rank2 ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::compare( $rank1, $rank2 ); + } + + /** + * @dataProvider compareProvider + */ + public function testCompare( $rank1, $rank2, $expected ) { + $this->assertSame( $expected, StatementRank::compare( $rank1, $rank2 ) ); + } + + public function compareProvider() { + return array( + array( null, null, 0 ), + array( null, 0, -1 ), + array( null, 1, -1 ), + array( null, 2, -1 ), + array( 0, null, 1 ), + array( 0, 0, 0 ), + array( 0, 1, -1 ), + array( 0, 2, -1 ), + array( 1, null, 1 ), + array( 1, 0, 1 ), + array( 1, 1, 0 ), + array( 1, 2, -1 ), + array( 2, null, 1 ), + array( 2, 0, 1 ), + array( 2, 1, 1 ), + array( 2, 2, 0 ), + ); + } + + /** + * @dataProvider neitherIntegerRangeNorNullProvider + */ + public function testGivenInvalidRank_findBestRankThrowsException( $rank ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::findBestRank( $rank ); + } + + /** + * @dataProvider invalidComparisonPairProvider + */ + public function testGivenInvalidArray_findBestRankThrowsException( $rank1, $rank2 ) { + $this->setExpectedException( 'InvalidArgumentException' ); + StatementRank::findBestRank( array( $rank1, $rank2 ) ); + } + + /** + * @dataProvider findBestRankProvider + */ + public function testFindBestRank( $ranks, $expected ) { + $this->assertSame( $expected, StatementRank::findBestRank( $ranks ) ); + } + + public function findBestRankProvider() { + return array( + array( null, null ), + array( 0, 0 ), + array( 1, 1 ), + array( array(), null ), + array( array( null ), null ), + array( array( 0 ), 0 ), + array( array( 1 ), 1 ), + array( array( null, 0 ), 0 ), + array( array( 0, null ), 0 ), + array( array( 0, 1 ), 1 ), + array( array( null, 0, 1, 2 ), 2 ), + array( array( 2, 1, 0, null ), 2 ), + ); + } + + public function integerRangeProvider() { + return array( + array( 0 ), + array( 1 ), + array( 2 ), + ); + } + + public function neitherIntegerRangeNorNullProvider() { + return array( + array( false ), + array( true ), + array( NAN ), + array( INF ), + array( '0' ), + array( '1' ), + array( 0.0 ), + array( 1.0 ), + array( -1 ), + array( 3 ), + ); + } + + public function notInIntegerRangeProvider() { + $invalid = $this->neitherIntegerRangeNorNullProvider(); + $invalid[] = array( null ); + return $invalid; + } + + public function invalidComparisonPairProvider() { + $invalid = $this->neitherIntegerRangeNorNullProvider(); + $pairs = array(); + + foreach ( $invalid as $args ) { + $pairs[] = array( 1, $args[0] ); + $pairs[] = array( $args[0], 1 ); + } + + return $pairs; + } + +} diff --git a/tests/unit/Statement/StatementTest.php b/tests/unit/Statement/StatementTest.php index 9df34f66..602501be 100644 --- a/tests/unit/Statement/StatementTest.php +++ b/tests/unit/Statement/StatementTest.php @@ -12,6 +12,7 @@ use Wikibase\DataModel\Snak\PropertyValueSnak; use Wikibase\DataModel\Snak\Snak; use Wikibase\DataModel\Snak\SnakList; +use Wikibase\DataModel\Statement\StatementRank; use Wikibase\DataModel\Statement\Statement; /** @@ -155,7 +156,7 @@ public function instanceProvider() { $instances[] = $baseInstance; $instance = clone $baseInstance; - $instance->setRank( Statement::RANK_PREFERRED ); + $instance->setRank( StatementRank::PREFERRED ); $instances[] = $instance; @@ -233,19 +234,15 @@ public function testAddNewReferenceWithAnArrayOfSnaks( Statement $statement ) { * @dataProvider instanceProvider */ public function testGetRank( Statement $statement ) { - $rank = $statement->getRank(); - $this->assertInternalType( 'integer', $rank ); - - $ranks = array( Statement::RANK_DEPRECATED, Statement::RANK_NORMAL, Statement::RANK_PREFERRED ); - $this->assertTrue( in_array( $rank, $ranks ), true ); + $this->assertTrue( StatementRank::isValid( $statement->getRank() ) ); } /** * @dataProvider instanceProvider */ public function testSetRank( Statement $statement ) { - $statement->setRank( Statement::RANK_DEPRECATED ); - $this->assertEquals( Statement::RANK_DEPRECATED, $statement->getRank() ); + $statement->setRank( StatementRank::DEPRECATED ); + $this->assertEquals( StatementRank::DEPRECATED, $statement->getRank() ); } /** @@ -388,7 +385,7 @@ public function notEqualsProvider() { $statementWithoutReferences->setReferences( new ReferenceList() ); $statementWithPreferredRank = $this->newStatement(); - $statementWithPreferredRank->setRank( Statement::RANK_PREFERRED ); + $statementWithPreferredRank->setRank( StatementRank::PREFERRED ); $statementMainSnakNotEqual = $this->newStatement(); $statementMainSnakNotEqual->setMainSnak( new PropertyNoValueSnak( 9000 ) ); @@ -412,7 +409,7 @@ private function newStatement() { ) ) ); - $statement->setRank( Statement::RANK_NORMAL ); + $statement->setRank( StatementRank::NORMAL ); return $statement; }