diff --git a/math/tests/data/BigNumberThrowTypes.php b/math/tests/data/BigNumberThrowTypes.php index 46bfc60..c04594e 100644 --- a/math/tests/data/BigNumberThrowTypes.php +++ b/math/tests/data/BigNumberThrowTypes.php @@ -8,6 +8,12 @@ use Brick\Math\BigInteger; use Brick\Math\BigNumber; use Brick\Math\BigRational; +use Brick\Math\Exception\DivisionByZeroException; +use Brick\Math\Exception\IntegerOverflowException; +use Brick\Math\Exception\InvalidArgumentException; +use Brick\Math\Exception\MathException; +use Brick\Math\Exception\NegativeNumberException; +use Brick\Math\Exception\RoundingNecessaryException; use Brick\Math\RoundingMode; use PHPStan\TrinaryLogic; @@ -44,6 +50,57 @@ public function ofWithString(string $s): void } } + public function ofWithStringCatchingDivisionByZero(string $s): void + { + try { + $result = BigInteger::of($s); + } catch (DivisionByZeroException) { + $result = BigInteger::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function ofFractionWithNonZeroDenominator(): void + { + try { + $result = BigRational::ofFraction(1, 2); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function ofFractionWithNonZeroDenominatorCatchingDivisionByZero(): void + { + try { + $result = BigRational::ofFraction(1, 2); + } catch (DivisionByZeroException) { + $result = BigRational::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function ofFractionWithMaybeZeroDenominator(int $denominator): void + { + try { + $result = BigRational::ofFraction(1, $denominator); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function ofFractionWithMaybeZeroDenominatorCatchingDivisionByZero(int $denominator): void + { + try { + $result = BigRational::ofFraction(1, $denominator); + } catch (DivisionByZeroException) { + $result = BigRational::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + // --- Conversion methods --- public function toBigIntegerOnBigInteger(BigInteger $a): void @@ -64,6 +121,39 @@ public function toBigDecimalOnBigRational(BigRational $a): void } } + public function toBigDecimalOnBigRationalCatchingRounding(BigRational $a): void + { + try { + $result = $a->toBigDecimal(); + } catch (RoundingNecessaryException) { + $result = BigDecimal::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function toIntOnBigIntegerCatchingOverflow(BigInteger $a): void + { + try { + $result = $a->toInt(); + } catch (IntegerOverflowException) { + $result = 0; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function toIntOnBigDecimalCatchingOverflow(BigDecimal $a): void + { + try { + $result = $a->toInt(); + } catch (IntegerOverflowException) { + $result = 0; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + // --- Operation methods --- public function plusWithSameType(BigInteger $a, BigInteger $b): void @@ -93,6 +183,17 @@ public function plusWithString(BigInteger $a, string $s): void } } + public function plusWithStringCatchingParsingException(BigInteger $a, string $s): void + { + try { + $result = $a->plus($s); + } catch (MathException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function quotientMayThrowDivisionByZero(BigInteger $a, BigInteger $b): void { try { @@ -102,6 +203,17 @@ public function quotientMayThrowDivisionByZero(BigInteger $a, BigInteger $b): vo } } + public function quotientMayThrowDivisionByZeroCatchingDivisionByZero(BigInteger $a, BigInteger $b): void + { + try { + $result = $a->quotient($b); + } catch (DivisionByZeroException) { + $result = BigInteger::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + /** @param int<1, max> $divisor */ public function quotientWithNonZeroDivisor(BigInteger $a, int $divisor): void { @@ -112,6 +224,17 @@ public function quotientWithNonZeroDivisor(BigInteger $a, int $divisor): void } } + public function quotientWithStringCatchingDivisionByZero(BigInteger $a, string $divisor): void + { + try { + $result = $a->quotient($divisor); + } catch (DivisionByZeroException) { + $result = BigInteger::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + public function compareToWithBigNumber(BigInteger $a, BigDecimal $b): void { try { @@ -189,6 +312,51 @@ public function toScaleWithUnnecessary(BigDecimal $a): void } } + /** @param int<0, max> $scale */ + public function toScaleWithNonNegativeScaleCatchingRounding(BigDecimal $a, int $scale): void + { + try { + $result = $a->toScale($scale, RoundingMode::Unnecessary); + } catch (RoundingNecessaryException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function toScaleWithMaybeNegativeScaleCatchingRounding(BigDecimal $a, int $scale): void + { + try { + $result = $a->toScale($scale, RoundingMode::Unnecessary); + } catch (RoundingNecessaryException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function toScaleWithSafeRoundingCatchingInvalidScale(BigDecimal $a, int $scale): void + { + try { + $result = $a->toScale($scale, RoundingMode::Down); + } catch (InvalidArgumentException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function toScaleWithMaybeNegativeScaleCatchingInvalidScale(BigDecimal $a, int $scale): void + { + try { + $result = $a->toScale($scale, RoundingMode::Down); + } catch (InvalidArgumentException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function dividedByWithSafeRounding(BigInteger $a, BigInteger $b): void { try { @@ -198,6 +366,17 @@ public function dividedByWithSafeRounding(BigInteger $a, BigInteger $b): void } } + public function dividedByWithSafeRoundingCatchingNonRoundingExceptions(BigInteger $a, BigInteger $b): void + { + try { + $result = $a->dividedBy($b, RoundingMode::Down); + } catch (MathException | DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + /** @param int<1, max> $divisor */ public function dividedByWithSafeRoundingAndNonZero(BigInteger $a, int $divisor): void { @@ -208,6 +387,28 @@ public function dividedByWithSafeRoundingAndNonZero(BigInteger $a, int $divisor) } } + public function dividedByWithMaybeZeroIntCatchingDivisionByZero(BigInteger $a, int $divisor): void + { + try { + $result = $a->dividedBy($divisor, RoundingMode::Down); + } catch (DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function sqrtWithSafeRoundingCatchingNegativeNumber(BigDecimal $a): void + { + try { + $result = $a->sqrt(2, RoundingMode::Down); + } catch (NegativeNumberException) { + $result = BigDecimal::zero(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + /** Static variadic methods (min, max, sum, gcdAll, lcmAll) */ public function minWithSameType(BigInteger $a, BigInteger $b): void diff --git a/money/tests/data/MoneyThrowTypes.php b/money/tests/data/MoneyThrowTypes.php index 0753125..6642238 100644 --- a/money/tests/data/MoneyThrowTypes.php +++ b/money/tests/data/MoneyThrowTypes.php @@ -6,12 +6,17 @@ use Brick\Math\BigDecimal; use Brick\Math\BigInteger; +use Brick\Math\Exception\DivisionByZeroException; +use Brick\Math\Exception\NumberFormatException; use Brick\Math\Exception\RoundingNecessaryException; use Brick\Math\RoundingMode; use Brick\Money\Context\CustomContext; use Brick\Money\Currency; use Brick\Money\CurrencyConverter; +use Brick\Money\Exception\ContextMismatchException; +use Brick\Money\Exception\CurrencyMismatchException; use Brick\Money\Exception\ExchangeRateException; +use Brick\Money\Exception\InvalidArgumentException; use Brick\Money\Exception\UnknownCurrencyException; use Brick\Money\Money; use Brick\Money\RationalMoney; @@ -50,6 +55,50 @@ public function ofStringWithKnownCurrency(string $amount): void } } + public function ofStringWithKnownCurrencyCatchingCurrencyAndRoundingExceptions(string $amount): void + { + try { + $result = Money::of($amount, 'USD'); + } catch (UnknownCurrencyException | RoundingNecessaryException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function ofIntWithUnknownCurrencyCatchingCurrencyException(string $code): void + { + try { + $result = Money::of(100, $code); + } catch (UnknownCurrencyException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function ofStringWithKnownCurrencyCatchingNumberAndRoundingExceptions(string $amount): void + { + try { + $result = Money::of($amount, 'USD'); + } catch (NumberFormatException | RoundingNecessaryException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function ofStringWithUnknownCurrencyCatchingNumberAndRoundingExceptions(string $amount, string $code): void + { + try { + $result = Money::of($amount, $code); + } catch (NumberFormatException | RoundingNecessaryException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + public function zeroWithKnownCurrency(): void { try { @@ -68,6 +117,17 @@ public function zeroWithUnknownCurrency(string $code): void } } + public function zeroWithUnknownCurrencyCatchingCurrencyException(string $code): void + { + try { + $result = Money::zero($code); + } catch (UnknownCurrencyException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function ofMinorIntWithKnownCurrency(): void { try { @@ -113,6 +173,28 @@ public function rationalMoneyOfWithSafeArgs(Currency $currency): void } } + public function rationalMoneyOfWithUnsafeAmountCatchingCurrencyException(string $amount, Currency $currency): void + { + try { + $result = RationalMoney::of($amount, $currency); + } catch (UnknownCurrencyException) { + $result = RationalMoney::of(100, 'USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function rationalMoneyOfWithUnknownCurrencyCatchingNumberException(string $code): void + { + try { + $result = RationalMoney::of(100, $code); + } catch (NumberFormatException) { + $result = RationalMoney::of(100, 'USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + public function ofZeroWithContext(): void { try { @@ -176,6 +258,17 @@ public function ofWithRoundingMode(BigDecimal $amount): void } } + public function ofDecimalWithoutRoundingModeCatchingNumberAndCurrencyExceptions(BigDecimal $amount): void + { + try { + $result = Money::of($amount, 'USD'); + } catch (NumberFormatException | UnknownCurrencyException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + public function ofMinorWithRoundingMode(BigDecimal $amount): void { try { @@ -205,6 +298,17 @@ public function compareToWithMoney(Money $a, Money $b): void } } + public function compareToWithMoneyCatchingCurrencyMismatch(Money $a, Money $b): void + { + try { + $result = $a->compareTo($b); + } catch (CurrencyMismatchException) { + $result = 0; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + // --- Arithmetic methods --- public function plusWithSafeRounding(Money $a): void @@ -225,6 +329,28 @@ public function dividedByWithSafeRounding(Money $a): void } } + public function plusWithUnsafeNumberCatchingRoundingException(Money $a, string $number): void + { + try { + $result = $a->plus($number, RoundingMode::Down); + } catch (RoundingNecessaryException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function plusWithMoneyCatchingMoneyExceptions(Money $a, Money $b): void + { + try { + $result = $a->plus($b, RoundingMode::Down); + } catch (CurrencyMismatchException | ContextMismatchException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function dividedByWithUnnecessaryRounding(Money $a): void { try { @@ -234,6 +360,37 @@ public function dividedByWithUnnecessaryRounding(Money $a): void } } + public function dividedByMaybeZeroIntWithSafeRounding(Money $a, int $divisor): void + { + try { + $result = $a->dividedBy($divisor, RoundingMode::Down); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function dividedByMaybeZeroIntWithSafeRoundingCatchingDivisionByZero(Money $a, int $divisor): void + { + try { + $result = $a->dividedBy($divisor, RoundingMode::Down); + } catch (DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function dividedByWithUnnecessaryRoundingCatchingDivisionByZero(Money $a): void + { + try { + $result = $a->dividedBy(3, RoundingMode::Unnecessary); + } catch (DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + // --- Int arithmetic (no rounding mode needed) --- public function multipliedByInt(Money $a): void @@ -301,6 +458,48 @@ public function rationalDividedByWithString(RationalMoney $a, string $s): void } } + public function rationalDividedByWithNonZeroInt(RationalMoney $a): void + { + try { + $result = $a->dividedBy(3); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function rationalDividedByWithMaybeZeroInt(RationalMoney $a, int $divisor): void + { + try { + $result = $a->dividedBy($divisor); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function rationalDividedByWithMaybeZeroIntCatchingDivisionByZero( + RationalMoney $a, + int $divisor, + ): void { + try { + $result = $a->dividedBy($divisor); + } catch (DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function rationalDividedByWithStringCatchingDivisionByZero(RationalMoney $a, string $s): void + { + try { + $result = $a->dividedBy($s); + } catch (DivisionByZeroException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + // --- Rounding mode methods --- public function toContextWithSafeRounding(Money $a): void @@ -312,6 +511,37 @@ public function toContextWithSafeRounding(Money $a): void } } + public function toContextWithUnnecessaryRounding(Money $a): void + { + try { + $result = $a->toContext($a->getContext(), RoundingMode::Unnecessary); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function convertedToWithSafeRoundingCatchingNonRoundingExceptions(Money $a, Currency $currency): void + { + try { + $result = $a->convertedTo($currency, 2, roundingMode: RoundingMode::Down); + } catch (UnknownCurrencyException | NumberFormatException | InvalidArgumentException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + + public function convertedToWithUnsafeRoundingCatchingNonRoundingExceptions(Money $a, Currency $currency): void + { + try { + $result = $a->convertedTo($currency, 2, roundingMode: RoundingMode::Unnecessary); + } catch (UnknownCurrencyException | NumberFormatException | InvalidArgumentException) { + $result = $a; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + // --- MoneyBag --- public function getMoneyWithKnownCurrency(\Brick\Money\MoneyBag $bag): void @@ -332,6 +562,17 @@ public function getMoneyWithUnknownCurrency(\Brick\Money\MoneyBag $bag, string $ } } + public function getMoneyWithUnknownCurrencyCatchingCurrencyException(\Brick\Money\MoneyBag $bag, string $code): void + { + try { + $result = $bag->getMoney($code); + } catch (UnknownCurrencyException) { + $result = $bag->getMoney('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + // --- CurrencyConverter --- public function convertWithSafeCurrency(CurrencyConverter $converter, Money $money): void @@ -365,6 +606,20 @@ public function convertWithUnknownCurrency(CurrencyConverter $converter, Money $ } } + public function convertWithUnknownCurrencyCatchingNonCurrencyExceptions( + CurrencyConverter $converter, + Money $money, + string $code, + ): void { + try { + $result = $converter->convert($money, $code); + } catch (ExchangeRateException | RoundingNecessaryException) { + $result = $money; + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + public function convertWithUnknownCurrencyAndSkippedNamedSafeRoundingMode( CurrencyConverter $converter, Money $money, @@ -388,6 +643,19 @@ public function convertToRationalWithSafeCurrency(CurrencyConverter $converter, } } + public function convertToRationalWithSafeCurrencyCatchingResidualException( + CurrencyConverter $converter, + Money $money, + ): void { + try { + $result = $converter->convertToRational($money, 'USD'); + } catch (ExchangeRateException) { + $result = $money->toRational(); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function convertToRationalWithUnknownCurrency(CurrencyConverter $converter, Money $money, string $code): void { try { @@ -397,6 +665,20 @@ public function convertToRationalWithUnknownCurrency(CurrencyConverter $converte } } + public function convertToRationalWithUnknownCurrencyCatchingNonCurrencyException( + CurrencyConverter $converter, + Money $money, + string $code, + ): void { + try { + $result = $converter->convertToRational($money, $code); + } catch (ExchangeRateException) { + $result = $money->toRational(); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + // --- RationalMoney::convertedTo --- public function rationalConvertedToWithSafeArgs(RationalMoney $a, Currency $currency): void @@ -408,6 +690,19 @@ public function rationalConvertedToWithSafeArgs(RationalMoney $a, Currency $curr } } + public function rationalConvertedToWithSafeArgsCatchingResidualException( + RationalMoney $a, + Currency $currency, + ): void { + try { + $result = $a->convertedTo($currency, 2); + } catch (InvalidArgumentException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createYes(), $result); + } + } + public function rationalConvertedToWithUnsafeCurrency(RationalMoney $a, string $code): void { try { @@ -416,4 +711,31 @@ public function rationalConvertedToWithUnsafeCurrency(RationalMoney $a, string $ assertVariableCertainty(TrinaryLogic::createMaybe(), $result); } } + + public function rationalConvertedToWithUnsafeCurrencyCatchingNonCurrencyExceptions( + RationalMoney $a, + string $code, + ): void { + try { + $result = $a->convertedTo($code, 2); + } catch (NumberFormatException | InvalidArgumentException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } + + public function rationalConvertedToWithUnsafeRateCatchingNonNumberExceptions( + RationalMoney $a, + Currency $currency, + string $rate, + ): void { + try { + $result = $a->convertedTo($currency, $rate); + } catch (UnknownCurrencyException | InvalidArgumentException) { + $result = Money::zero('USD'); + } finally { + assertVariableCertainty(TrinaryLogic::createMaybe(), $result); + } + } }