diff --git a/Src/Domain/.github/workflows/cicd.yml b/Src/Domain/.github/workflows/cicd.yml new file mode 100644 index 0000000..639319d --- /dev/null +++ b/Src/Domain/.github/workflows/cicd.yml @@ -0,0 +1,14 @@ +name: CICD + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Run composer install + run: composer install -n --prefer-dist + - name: Run tests + run: ./vendor/bin/phpunit diff --git a/Src/Domain/Decimal.php b/Src/Domain/Decimal.php new file mode 100644 index 0000000..f2f6694 --- /dev/null +++ b/Src/Domain/Decimal.php @@ -0,0 +1,87 @@ +setDecimal($decimal); + } + + public static function fromString(string $decimal): self + { + return new self($decimal); + } + + private function setDecimal(string $decimal): void + { + if (!$this->validateDecimal($decimal)) { + throw new DecimalInvalidArgument( + sprintf('Invalid decimal part %1$s. Is not numeric', $decimal) + ); + } + $this->decimal = $decimal; + } + + public function __toString(): string + { + return $this->decimal; + } + + public function getDecimals(): string + { + return $this->decimal; + } + + public function equals(DecimalInterface $numericPart): bool + { + return $this->getDecimals() === $numericPart->getDecimals(); + } + + public function __invoke(): string + { + return $this->getDecimals(); + } + + private function validateDecimal(string $number): bool + { + if (self::EMPTY_STRING === $number) { + return true; + } + $this->mustBeNumeric($number); + + return $this->validValueForADecimal($number); + } + + private function mustBeNumeric(string $number) + { + if (!is_numeric($number)) { + throw new DecimalInvalidArgument( + sprintf('Invalid decimal part %1$s. Is not numeric', $number) + ); + } + } + + private function validValueForADecimal($number): bool + { + try { + $number = (string) $number; + } catch (\Exception $e) { + throw new DecimalInvalidArgument( + sprintf('Invalid decimal part %1$s. Is not numeric', $number) + ); + } + + return (bool) preg_match_all(self::VALIDATOR_REGEX, $number, $matches, PREG_SET_ORDER, 0); + } +} diff --git a/Src/Domain/DecimalInterface.php b/Src/Domain/DecimalInterface.php new file mode 100644 index 0000000..fbf85f2 --- /dev/null +++ b/Src/Domain/DecimalInterface.php @@ -0,0 +1,12 @@ +setInteger($integer); + } + + public static function fromString(string $integer) + { + return new self($integer); + } + + private function setInteger(string $integer) + { + $this->validateInteger($integer); + $this->integer = $this->cleanLeadingZero($integer); + } + + private function validateInteger(string $integer) + { + if ($this->isIntegerEmpty($integer)) { + throw new IntegerInvalidArgument( + sprintf(self::EMPTY_STRING_MGS, $integer) + ); + } + $this->validValueForAInteger($integer); + } + + private function validValueForAInteger($integer) + { + if (!preg_match(self::VALIDATOR_REGEX, $integer)) { + throw new IntegerInvalidArgument( + sprintf(self::INVALID_INTEGER_PART_MGS, $integer) + ); + } + } + + private function isIntegerEmpty($integer): bool + { + if (self::EMPTY_STRING === $integer) { + return true; + } + return false; + } + + private function cleanLeadingZero($integer): string + { + if (preg_match(self::LEADING_ZERO_WITH_SIGN_VALIDATOR_REGEX, $integer)) { + $integer = $integer[0] . ltrim(substr($integer, 1), self::ZERO_STRING); + } else { + $integer = ltrim($integer, self::ZERO_STRING); + } + + if (preg_match(self::SIGN_REGEX, $integer) && strlen($integer) == 1) { + $integer = $integer[0] . self::ZERO_STRING; + } + + if ($this->isIntegerEmpty($integer)) { + $integer = self::ZERO_STRING; + } + + return $integer; + } + + public function __invoke(): string + { + return $this->getInteger(); + } + + public function __toString(): string + { + return $this->integer; + } + + public function getInteger(): string + { + return $this->integer; + } + + public function equals(IntegerInterface $integer): bool + { + return $this->getInteger() === $integer->getInteger(); + } +} diff --git a/Src/Domain/IntegerInterface.php b/Src/Domain/IntegerInterface.php new file mode 100644 index 0000000..65eabec --- /dev/null +++ b/Src/Domain/IntegerInterface.php @@ -0,0 +1,10 @@ +setIntegerPart($integerPart); + } + + private function setIntegerPart(string $integerPart) + { + $this->integerPart = $this->parseIntegerPart($integerPart); + } + + private function parseIntegerPart(string $integer): string + { + $default = $this->defaultValuesForIntegerValidation($integer); + if (null !== $default) { + return $default; + } + + $this->checkNumber($integer); + + return $integer; + } + + private function defaultValuesForIntegerValidation(string $integer): ?string + { + if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + return self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + if (self::NEGATIVE_SIGN === $integer) { + return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + return null; + } + + private function checkNumber(string $integer): void + { + $nonZero = false; + $characters = strlen($integer); + for ($position = 0; $position < $characters; ++$position) { + $digit = $integer[$position]; + + if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { + throw new \InvalidArgumentException( + sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + ); + } + + if (false === $nonZero && '0' === $digit) { + throw new \InvalidArgumentException( + 'Leading zeros are not allowed' + ); + } + + $nonZero = true; + } + } + + public function isNegative(): bool + { + return self::NEGATIVE_SIGN === $this->integerPart[0]; + } + + public function __invoke(): string + { + $this->getIntegerPart(); + } + + public function getIntegerPart(): string + { + return $this->integerPart; + } + + public function equals (self $integerPart): bool + { + return $this->getIntegerPart() === $integerPart->getIntegerPart(); + } +} \ No newline at end of file diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index c8a927c..27c474a 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -4,31 +4,36 @@ namespace WeDev\Price\Domain; +use WeDev\Price\Domain\Exception\NumberInvalidArgument; + final class Number { - private $integerPart; - - private $fractionalPart; + private $integer; + private $decimals; private const EMPTY_STRING = ''; private const FLOAT_FORMAT = '%.14F'; private const NEGATIVE_SIGN = '-'; private const NUMERIC_SEPARATOR = '.'; - private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const FIRST_CHART = '0'; + private const HALF_DECIMAL_VALUE = '5'; + private const VALIDATOR_REGEX = '/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/m'; + private const VALID_NUMBER_MSG = 'Valid numeric value expected'; - private const VALID_NUMBERS = [0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; - - private function __construct(string $integerPart, string $fractionalPart = '') + private function __construct(string $integer, string $decimals = '') { - $this->preconditionNotNullArguments($integerPart, $fractionalPart); + $this->notNullArguments($integer, $decimals); - $this->integerPart = $this->parseIntegerPart((string) $integerPart); - $this->fractionalPart = $this->parseFractionalPart((string) $fractionalPart); + $this->integer = Integer::fromString($integer); + $this->decimals = Decimal::fromString($decimals); } - public static function fromString(string $number): self + private static function fromString(string $number): self { + if (!static::validNumber($number)) { + throw new NumberInvalidArgument(self::VALID_NUMBER_MSG); + } + $decimalSeparatorPosition = strpos($number, self::NUMERIC_SEPARATOR); if (false === $decimalSeparatorPosition) { return new self($number, self::EMPTY_STRING); @@ -40,15 +45,6 @@ public static function fromString(string $number): self ); } - public static function fromFloat(float $number): self - { - if (false === is_float($number)) { - throw new \InvalidArgumentException('Floating point value expected'); - } - - return self::fromString(sprintf(self::FLOAT_FORMAT, $number)); - } - /** * @param float|int|string $number * @@ -68,7 +64,7 @@ public static function fromNumber($number): self return self::fromString($number); } - throw new \InvalidArgumentException('Valid numeric value expected'); + throw new NumberInvalidArgument(self::VALID_NUMBER_MSG); } public function __invoke(): string @@ -78,137 +74,87 @@ public function __invoke(): string public function __toString(): string { - if (self::EMPTY_STRING === $this->fractionalPart) { - return $this->integerPart; + if (self::EMPTY_STRING === $this->getDecimals()) { + return $this->getInteger(); } - return $this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart; + return $this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals(); } public function isDecimal(): bool { - return self::EMPTY_STRING !== $this->fractionalPart; + return self::EMPTY_STRING !== $this->decimals; } public function isInteger(): bool { - return self::EMPTY_STRING === $this->fractionalPart; + return self::EMPTY_STRING === $this->decimals; } public function isHalf(): bool { - return '5' === $this->fractionalPart; + return self::HALF_DECIMAL_VALUE === $this->getDecimals(); } public function isCurrentEven(): bool { - $lastIntegerPartNumber = $this->integerPart[strlen($this->integerPart) - 1]; + $lastIntegerNumber = $this->integer[strlen($this->integer) - 1]; - return 0 === $lastIntegerPartNumber % 2; + return 0 === $lastIntegerNumber % 2; } public function isCloserToNext(): bool { - if (self::EMPTY_STRING === $this->fractionalPart) { + if (self::EMPTY_STRING === $this->getDecimals()) { return false; } - return $this->fractionalPart[0] >= 5; + return (int) $this->getDecimals()[0] >= 5; } public function toFloat(): float { - return (self::EMPTY_STRING !== $this->fractionalPart) ? - (float) ($this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart) : - (float) $this->integerPart; + return (self::EMPTY_STRING !== $this->getDecimals()) ? + (float) ($this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals()) : + (float) $this->integer; } public function isNegative(): bool { - return self::NEGATIVE_SIGN === $this->integerPart[0]; + return self::NEGATIVE_SIGN === $this->integer[0]; } - public function getIntegerPart(): string + public function getInteger(): string { - return $this->integerPart; + return $this->integer->__toString(); } - public function getFractionalPart(): string + public function getDecimals(): string { - return $this->fractionalPart; - } - - private function parseIntegerPart(string $number): string - { - $default = $this->defaultValuesForIntegerValidation($number); - if (null !== $default) { - return $default; - } - - $nonZero = false; - $characters = strlen($number); - for ($position = 0; $position < $characters; ++$position) { - $digit = $number[$position]; - - if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new \InvalidArgumentException( - sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $number, $digit) - ); - } - - if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException( - 'Leading zeros are not allowed' - ); - } - - $nonZero = true; - } - - return $number; + return $this->decimals->__toString(); } - private function defaultValuesForIntegerValidation(string $number): ?string + public function equals(self $number): bool { - if (self::EMPTY_STRING === $number || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $number) { - return self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - if (self::NEGATIVE_SIGN === $number) { - return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - return null; + return $this->getInteger() === $number->getInteger() && + $this->getDecimals() === $number->getDecimals(); } - private function parseFractionalPart(string $number): string + private function validNumber($number): bool { - if (self::EMPTY_STRING === $number) { - return $number; - } - - for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) { - $digit = $number[$position]; - if (!isset(self::VALID_NUMBERS[$digit])) { - throw new \InvalidArgumentException( - sprintf('Invalid fractional part %1$s. Invalid digit %2$s found', $number, $digit) - ); - } + try { + $number = (string) $number; + } catch (\Exception $e) { + return false; } - return $number; - } - - public function equal(self $number): bool - { - return $this->integerPart === $number->getIntegerPart() && - $this->fractionalPart === $number->getFractionalPart(); + return (bool) preg_match_all(self::VALIDATOR_REGEX, $number, $matches, PREG_SET_ORDER, 0); } - private function preconditionNotNullArguments(string $integerPart, string $fractionalPart) + private function notNullArguments(string $integerPart, string $fractionalPart) { if (self::EMPTY_STRING === $integerPart && self::EMPTY_STRING === $fractionalPart) { - throw new \InvalidArgumentException('Empty number is invalid'); + throw new NumberInvalidArgument('An empty number is invalid'); } } } diff --git a/Src/Domain/NumberFormatDecorator.php b/Src/Domain/NumberFormatDecorator.php index 9646971..01bda36 100644 --- a/Src/Domain/NumberFormatDecorator.php +++ b/Src/Domain/NumberFormatDecorator.php @@ -32,7 +32,7 @@ public function roundedNumber(float $round = 0.5): Number } if (!is_null($round) && !is_null($result) && mb_strlen($result) > mb_strlen($round)) { - $result = $this->numberFormat($result, strlen(($this->number->getFractionalPart()))); + $result = $this->numberFormat($result, strlen(($this->number->getDecimals()))); } return Number::fromFloat($result); diff --git a/Src/Domain/Numerable.php b/Src/Domain/Numerable.php new file mode 100644 index 0000000..8711633 --- /dev/null +++ b/Src/Domain/Numerable.php @@ -0,0 +1,16 @@ +integer = $integer; - $this->decimal = $decimal; - } - - public function getFloatingPrice(): float - { - return (float) ($this->integer . '.' . $this->decimal); - } - - public function __toString() - { - return (string) $this->integer . '.' . $this->decimal; - } - - public static function createPrice(int $integer, int $decimal): self - { - return new static($integer, $decimal); - } -} diff --git a/Src/Domain/PriceAble.php b/Src/Domain/PriceAble.php deleted file mode 100644 index 5df8bd6..0000000 --- a/Src/Domain/PriceAble.php +++ /dev/null @@ -1,10 +0,0 @@ -setPrice($price); - } - - private function setPrice(string $price): void - { - $this->price = $price; - } - - public function __toString(): string - { - return $this->price; - } -} diff --git a/Tests/DecimalTest.php b/Tests/DecimalTest.php new file mode 100644 index 0000000..d65597e --- /dev/null +++ b/Tests/DecimalTest.php @@ -0,0 +1,91 @@ +expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::A_BAD_FORMATTED_DECIMAL); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalTwoFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_2); + } + + /** + * @test + */ + public function shouldCreateAWellFormattedDecimal(): void + { + $decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $this->assertTrue(self::A_WELL_FORMATTED_DECIMAL === $decimal->getDecimals()); + } + + /** + * @test + */ + public function shouldBeEqual(): void + { + $a_decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $an_other_decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $this->assertTrue($an_other_decimal->equals($a_decimal)); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalFourFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_4); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalTheeFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_3); + } + + /** + * @test + */ + public function shouldNotCreatDecimalFromAString(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::A_RANDOM_STRING); + } +} diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php new file mode 100644 index 0000000..2c929d8 --- /dev/null +++ b/Tests/IntegerTest.php @@ -0,0 +1,191 @@ +expectException(IntegerInvalidArgument::class); + Integer::fromString(self::AN_EMPTY_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromARandomString() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_RANDOM_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPosiiveInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_SIGN); + } + + /** + * @test + */ + public function shouldCreateAnIntegerWithoutLeadingZeroFromABadFormattedIntegerWithLeadingZeros() + { + $integer = Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + $intWithoutLeadingZero = ltrim(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS, self::A_STRING_INTEGER_ZERO); + $this->assertTrue((string) $intWithoutLeadingZero === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotTheSameIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() + { + $integer = Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotTheSameIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() + { + $integer = Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZerosAndSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_AND_SIGN); + } + + /** + * @test + */ + public function shouldNotCreateANumericIntegerFromAWellFormattedNumericInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertFalse(self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerStringFromAWellFormattedRealInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertTrue((string) self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue(self::A_WELL_FORMATTED_STRING_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldBeEqual(): void + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $other_Integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue($other_Integer->equals($integer)); + } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringIntegerZero(): void + { + $integer = Integer::fromString(self::A_STRING_INTEGER_ZERO); + $this->assertTrue(self::A_STRING_INTEGER_ZERO === $integer->getInteger()); + } +} diff --git a/Tests/NumberTest.php b/Tests/NumberTest.php index 06b6c53..3039a52 100644 --- a/Tests/NumberTest.php +++ b/Tests/NumberTest.php @@ -3,6 +3,7 @@ namespace WeDev\Price\Tests; use PHPUnit\Framework\TestCase; +use WeDev\Price\Domain\Exception\NumberInvalidArgument; use WeDev\Price\Domain\Number; class NumberTest extends TestCase @@ -50,19 +51,10 @@ class NumberTest extends TestCase /** * @test */ - public function typeErrorForNamedConstructorFloat() + public function shouldThrowInvalidArgumentForNamedConstructorString() { - $this->expectException(\TypeError::class); - Number::fromFloat(null); - } - - /** - * @test - */ - public function typeErrorForNamedConstructorString() - { - $this->expectException(\TypeError::class); - Number::fromString(null); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(null); } /** @@ -70,7 +62,7 @@ public function typeErrorForNamedConstructorString() */ public function typeErrorForNamedConstructorNumber() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(null); } @@ -79,17 +71,17 @@ public function typeErrorForNamedConstructorNumber() */ public function shouldThrowInvalidArgumentExceptionOnEmptyString() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::EMPTY_STRING); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::EMPTY_STRING); } /** * @test */ - public function shouldThrowTypeErrorOnEmptyArray() + public function shouldThrowInvalidArgumentOnEmptyArray() { - $this->expectException(\TypeError::class); - Number::fromString(self::EMPTY_ARRAY); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::EMPTY_ARRAY); } /** @@ -97,8 +89,8 @@ public function shouldThrowTypeErrorOnEmptyArray() */ public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberString() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::A_BAD_FORMATTED_NUMBER_ARRAY); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::A_BAD_FORMATTED_NUMBER_ARRAY); } /** @@ -106,8 +98,8 @@ public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberStrin */ public function shouldNotCreateANumberFromAStringWithTwoPoints() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::AN_OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_POINTS_STRING); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::AN_OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_POINTS_STRING); } /** @@ -115,8 +107,8 @@ public function shouldNotCreateANumberFromAStringWithTwoPoints() */ public function shouldNotCreateANumberFromAStringWithPointAndComma() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::OTHER_BAD_FORMATTED_NUMBER_STRING); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_STRING); } /** @@ -124,8 +116,8 @@ public function shouldNotCreateANumberFromAStringWithPointAndComma() */ public function shouldNotCreateANumberFromAStringWithTwoCommas() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_COMMAS_STRING); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_COMMAS_STRING); } //************************** @@ -137,8 +129,8 @@ public function shouldNotCreateANumberFromAStringWithTwoCommas() */ public function shouldThrowInvalidArgumentExceptionOnEmptyStringWithSpaces() { - $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::EMPTY_STRING_WITH_SPACES); + $this->expectException(NumberInvalidArgument::class); + Number::fromNumber(self::EMPTY_STRING_WITH_SPACES); } /** @@ -146,7 +138,7 @@ public function shouldThrowInvalidArgumentExceptionOnEmptyStringWithSpaces() */ public function shouldCreateNumberFromAPositiveDecimalString() { - $number = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -155,7 +147,7 @@ public function shouldCreateNumberFromAPositiveDecimalString() */ public function shouldCreateNumberFromANegativeDecimalString() { - $number = Number::fromString(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::A_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -164,7 +156,7 @@ public function shouldCreateNumberFromANegativeDecimalString() */ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPartString() { - $number = Number::fromString(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -173,7 +165,7 @@ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPartStri */ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPartString() { - $number = Number::fromString(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -182,7 +174,7 @@ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPartStri */ public function shouldCreateNumberFromAPositiveIntegerString() { - $number = Number::fromString(self::A_POSITIVE_INTEGER_STRING); + $number = Number::fromNumber(self::A_POSITIVE_INTEGER_STRING); $this->assertTrue(self::A_POSITIVE_INTEGER_STRING === $number->__toString()); } @@ -191,7 +183,7 @@ public function shouldCreateNumberFromAPositiveIntegerString() */ public function shouldCreateNumberFromANegativeIntegerString() { - $number = Number::fromString(self::A_NEGATIVE_INTEGER_STRING); + $number = Number::fromNumber(self::A_NEGATIVE_INTEGER_STRING); $this->assertTrue(self::A_NEGATIVE_INTEGER_STRING === $number->__toString()); } @@ -200,7 +192,7 @@ public function shouldCreateNumberFromANegativeIntegerString() */ public function shouldRemoveTailingZerosFromFloat() { - $number = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -209,8 +201,8 @@ public function shouldRemoveTailingZerosFromFloat() */ public function shouldCompareTwoStringFloatsAsEquals() { - $number_a = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $number_b = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -219,8 +211,8 @@ public function shouldCompareTwoStringFloatsAsEquals() */ public function shouldCompareTowStringFloatOneWithTailingZerosAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -233,7 +225,7 @@ public function shouldCompareTowStringFloatOneWithTailingZerosAsEquals() */ public function shouldCreateNumberFromAPositiveFloat() { - $number = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === (string) $number); } @@ -242,7 +234,7 @@ public function shouldCreateNumberFromAPositiveFloat() */ public function shouldCreateNumberFromAPositiveDecimal() { - $number = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === (string) $number); } @@ -251,7 +243,7 @@ public function shouldCreateNumberFromAPositiveDecimal() */ public function shouldCreateNumberFromANegativeDecimal() { - $number = Number::fromFloat(self::A_NEGATIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -260,7 +252,7 @@ public function shouldCreateNumberFromANegativeDecimal() */ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPart() { - $number = Number::fromFloat(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -269,7 +261,7 @@ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPart() */ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPart() { - $number = Number::fromFloat(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); $this->assertTrue(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -278,7 +270,7 @@ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPart() */ public function shouldCreateNumberFromAPositiveInteger() { - $number = Number::fromFloat(self::A_POSITIVE_INTEGER); + $number = Number::fromNumber(self::A_POSITIVE_INTEGER); $this->assertTrue(self::A_POSITIVE_INTEGER_STRING === $number->__toString()); } @@ -287,7 +279,7 @@ public function shouldCreateNumberFromAPositiveInteger() */ public function shouldCreateNumberFromANegativeInteger() { - $number = Number::fromFloat(self::A_NEGATIVE_INTEGER); + $number = Number::fromNumber(self::A_NEGATIVE_INTEGER); $this->assertTrue(self::A_NEGATIVE_INTEGER_STRING === $number->__toString()); } @@ -296,8 +288,8 @@ public function shouldCreateNumberFromANegativeInteger() */ public function shouldCompareTwoFloatAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -306,8 +298,8 @@ public function shouldCompareTwoFloatAsEquals() */ public function shouldCompareTwoFloatOneWithTailingZerosAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -320,9 +312,9 @@ public function shouldCompareTwoFloatOneWithTailingZerosAsEquals() */ public function shouldBeEqualsComparingTwoEqualAndPositiveFloats() { - $number_a = Number::fromFloat(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertFalse($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $this->assertFalse($number_a->equals($number_b)); } /** @@ -330,9 +322,9 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloats() */ public function shouldNotBeEqualsComparingTwoDifferentAndPositiveFloats() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -342,7 +334,7 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloatsWithNumberConstr { $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -350,9 +342,9 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloatsWithNumberConstr */ public function shouldBeEqualsComparingTwoEqualFloatsWithStringConstructor() { - $number_a = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $number_b = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -362,7 +354,7 @@ public function shouldBeEqualTwoNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -372,7 +364,7 @@ public function shouldBeEqualTwoNegativeNumbersUnderOneWithTailingZeros() { $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -382,7 +374,7 @@ public function shouldBeEqualTwoNegativeNumbersUnderOneWithTailingZerosString() { $number_a = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -392,7 +384,7 @@ public function shouldBeEqualTwoPositiveNumbersUnderOneWithTailingZerosString() { $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -402,7 +394,7 @@ public function shouldBeEqualTwoPositiveNumbersUnderOneWithTailingZeros() { $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -412,7 +404,7 @@ public function shouldBeEqualTwoNegativeNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -422,7 +414,7 @@ public function shouldBeEqualTwoNegativeDecimalNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -496,4 +488,76 @@ public function shouldNotBehalfByNegativeDecimalNumberBiggerThanHalfString() $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); $this->assertFalse($number_a->isHalf()); } + + /** + * @test + */ + public function shouldBeCloseToNextNumberFloat() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNumberFloat() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNegativeNumberFloat() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNegativeNumberFloat() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNumberString() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNumberString() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF_STRING); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNegativeNumberString() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF_STRING); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNegativeNumberString() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); + $this->assertTrue($number_a->isCloserToNext()); + } }