mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Fix several problems with FixedPointConversion generation code. The first problem is that at some point `repr(value)` was being used, which turn the number into a string. That was great for printing the number, but make the test against the value of the number (like `testValue < otherMin` always false. There were a number of tests that were never performed, specifically the integer tests. The second problem was using doubles in the Python code. For Float32 and Float64 the tests were generated correctly, but in the case of Float80, the test adding or removing a quantity to the maximum/minimum were failing because of the lack of precission (Adding 0.1 to a very big/small number is the same as not adding anything). Switching to Decimal should keep enough precission for the tests. Finally the last problem was that the bounds of the conversions are not actually `selfMin` and `selfMax`, but the values returned by the utility function `getFtoIBounds`. For example for unsigned types, the lower bound is always -1, not zero (every value between -1 and zero is rounded to zero, and doesn't fail). Instead of using nested gyb templates, use lit.cfg %target-ptrsize, which should be faster, cleaner, and provides correct line-directive output. Remove a bunch of warnings in Swift when compiling the generated result of FixedPointConversion.swift.gyb. Co-authored-by: Gwynne Raskind <gwynne@users.noreply.github.com>
228 lines
7.7 KiB
Swift
228 lines
7.7 KiB
Swift
// FIXME(integers): add tests that perform the same checks in generic code
|
|
|
|
%{
|
|
from SwiftIntTypes import all_integer_types, int_max, int_min
|
|
from SwiftFloatingPointTypes import all_floating_point_types, getFtoIBounds
|
|
|
|
from decimal import Decimal
|
|
}%
|
|
|
|
import StdlibUnittest
|
|
|
|
var FixedPointConversionTraps = TestSuite("FixedPointToFixedPointConversionTraps")
|
|
var FixedPointConversionFailure = TestSuite("FixedPointToFixedPointConversionFailures")
|
|
|
|
var FloatingPointConversionTruncations = TestSuite("FloatingPointToFixedPointConversionTruncations")
|
|
var FloatingPointConversionTraps = TestSuite("FloatingPointConversionTraps")
|
|
var FloatingPointConversionFailures = TestSuite("FloatingPointToFixedPointConversionFailures")
|
|
|
|
func getInfiniteOrNaNMessage() -> String {
|
|
if _isDebugAssertConfiguration() {
|
|
return "either infinite or NaN"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getTooSmallMessage() -> String {
|
|
if _isDebugAssertConfiguration() {
|
|
return "would be less than"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getTooLargeMessage() -> String {
|
|
if _isDebugAssertConfiguration() {
|
|
return "would be greater than"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
% word_bits = int(target_ptrsize)
|
|
% for self_ty in all_integer_types(word_bits):
|
|
% selfBits = self_ty.bits
|
|
% selfSigned = self_ty.is_signed
|
|
% selfMin = self_ty.min
|
|
% selfMax = self_ty.max
|
|
% Self = self_ty.stdlib_name
|
|
|
|
% # Test conversion behaviors for all integer types
|
|
% for other_ty in all_integer_types(word_bits):
|
|
% otherBits = other_ty.bits
|
|
% otherSigned = other_ty.is_signed
|
|
% otherMin = other_ty.min
|
|
% otherMax = other_ty.max
|
|
% Other = other_ty.stdlib_name
|
|
|
|
% for testValue in [selfMin, selfMax, selfMin - 1, selfMax + 1, otherMin, otherMax]:
|
|
|
|
% if testValue < otherMin or testValue > otherMax:
|
|
% # Can't construct `other` value, do nothing and continue.
|
|
% pass
|
|
% elif testValue >= selfMin and testValue <= selfMax:
|
|
% # Test value can be represented by Self, test conversion succeeds
|
|
|
|
/// Always-safe conversion from ${Other}(${testValue}) to ${Self}.
|
|
FixedPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValue}") {
|
|
// Test that nothing interesting happens and we end up with the same result after converting.
|
|
let input = get${Other}(${testValue})
|
|
expectEqual(${testValue}, ${Self}(input))
|
|
}
|
|
|
|
/// Never-nil failable conversion from ${Other}(${testValue}) to ${Self}.
|
|
FixedPointConversionFailure.test("${Other}To${Self}FailableConversion/dest=${testValue}") {
|
|
// Test that nothing interesting happens and we end up with a non-nil, identical result.
|
|
let input = get${Other}(${testValue})
|
|
let result = ${Self}(exactly: input)
|
|
expectEqual(${testValue}, result)
|
|
}
|
|
|
|
% else:
|
|
% # Test value is out of range of Self, test conversion fails
|
|
|
|
/// Always-failing conversion from ${Other}(${testValue}) to ${Self}.
|
|
FixedPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValue}") {
|
|
// Test that we check if we fail and crash when an integer would be truncated in conversion.
|
|
let input = get${Other}(${testValue})
|
|
expectCrashLater()
|
|
let result = ${Self}(input)
|
|
_blackHole(result)
|
|
}
|
|
|
|
/// Always-nil failable conversion from ${Other}(${testValue}) to ${Self}.
|
|
FixedPointConversionFailure.test("${Other}To${Self}Conversion/dest=${testValue}") {
|
|
// Test that we check if we return nil when an integer would be truncated in conversion.
|
|
let input = get${Other}(${testValue})
|
|
expectNil(${Self}(exactly: input))
|
|
}
|
|
|
|
% end
|
|
% end # for testValue in ...
|
|
% end # for in all_integer_types (Other)
|
|
|
|
% # Test conversion behaviors for all floating-point types
|
|
% for other_type in all_floating_point_types():
|
|
% Other = "Float" + str(other_type.bits)
|
|
% otherMin = -int_max(bits=other_type.explicit_significand_bits, signed=False)
|
|
% otherMax = int_max(bits=other_type.explicit_significand_bits, signed=False)
|
|
% (selfFtoIMin, selfFtoIMax) = getFtoIBounds(other_type.bits, selfBits, selfSigned)
|
|
|
|
% if Other == 'Float80':
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
% end
|
|
|
|
% testValues = [
|
|
% Decimal(selfMin),
|
|
% Decimal(selfMax),
|
|
% Decimal(selfFtoIMin) - Decimal('0.1'),
|
|
% Decimal(selfFtoIMax) + Decimal('0.1'),
|
|
% Decimal(otherMin),
|
|
% Decimal(otherMax),
|
|
% Decimal('0.0'),
|
|
% Decimal('-0.0'),
|
|
% Decimal('0.1'),
|
|
% Decimal('-0.1')
|
|
% ]
|
|
% for testValue in testValues:
|
|
% testValueStr = str(testValue)
|
|
|
|
% if testValue < otherMin or testValue > otherMax:
|
|
% # Can't construct `other` value to test from, do nothing and continue.
|
|
% pass
|
|
% elif testValue >= selfFtoIMin and testValue <= selfFtoIMax and (testValue % 1).is_zero():
|
|
% # Test value can be represented exactly by Self, test two-way conversion
|
|
|
|
FloatingPointConversionTruncations.test("${Other}To${Self}Conversion/dest=${testValueStr}") {
|
|
let input = get${Other}(${testValueStr})
|
|
let result = ${Self}(input)
|
|
let resultConvertedBack = ${Other}(result)
|
|
expectEqual(${testValueStr}, resultConvertedBack)
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Other}To${Self}FailableConversion/dest=${testValueStr}") {
|
|
let input = get${Other}(${testValueStr})
|
|
expectNotNil(${Self}(exactly: input))
|
|
}
|
|
|
|
% else:
|
|
% if testValue > selfFtoIMax:
|
|
% # Test value exceeds maximum value of Self, test for too large message
|
|
FloatingPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValueStr}")
|
|
.crashOutputMatches(getTooLargeMessage()).code {
|
|
expectCrashLater()
|
|
% elif testValue < selfFtoIMin:
|
|
% # Test value doesn't reach minimum value of Self, test for too small message
|
|
FloatingPointConversionTraps.test("${Other}To${Self}Conversion/dest=${testValueStr}")
|
|
.crashOutputMatches(getTooSmallMessage()).code {
|
|
expectCrashLater()
|
|
% else:
|
|
% # Test value can be represented inexactly by Self, test for truncation
|
|
FloatingPointConversionTruncations.test("${Other}To${Self}Conversion/dest=${testValueStr}") {
|
|
% end
|
|
let input = get${Other}(${testValueStr})
|
|
let result = ${Self}(input)
|
|
let resultConvertedBack = ${Other}(result)
|
|
expectNotEqual(input, resultConvertedBack)
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Other}To${Self}Conversion/dest=${testValueStr}") {
|
|
let input = get${Other}(${testValueStr})
|
|
expectNil(${Self}(exactly: input))
|
|
}
|
|
% end
|
|
% end # for in testValues
|
|
|
|
// Test Always-Trapping conversions.
|
|
|
|
% if not selfSigned:
|
|
|
|
FloatingPointConversionTraps.test("${Self}/${Other}/negative")
|
|
.crashOutputMatches(getTooSmallMessage()).code {
|
|
expectCrashLater()
|
|
_blackHole(${Self}(get${Other}(-123.0)))
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Self}/${Other}/negative") {
|
|
expectNil(${Self}(exactly: get${Other}(-123.0)))
|
|
}
|
|
|
|
% end
|
|
|
|
FloatingPointConversionTraps.test("${Self}/${Other}/+inf")
|
|
.crashOutputMatches(getInfiniteOrNaNMessage()).code {
|
|
expectCrashLater()
|
|
_blackHole(${Self}(get${Other}(${Other}.infinity)))
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Self}/${Other}/+inf") {
|
|
expectNil(${Self}(exactly: get${Other}(${Other}.infinity)))
|
|
}
|
|
|
|
FloatingPointConversionTraps.test("${Self}/${Other}/-inf")
|
|
.crashOutputMatches(getInfiniteOrNaNMessage()).code {
|
|
expectCrashLater()
|
|
_blackHole(${Self}(get${Other}(-${Other}.infinity)))
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Self}/${Other}/-inf") {
|
|
expectNil(${Self}(exactly: get${Other}(-${Other}.infinity)))
|
|
}
|
|
|
|
FloatingPointConversionTraps.test("${Self}/${Other}/NaN")
|
|
.crashOutputMatches(getInfiniteOrNaNMessage()).code {
|
|
expectCrashLater()
|
|
_blackHole(${Self}(get${Other}(${Other}.nan)))
|
|
}
|
|
|
|
FloatingPointConversionFailures.test("${Self}/${Other}/NaN") {
|
|
expectNil(${Self}(exactly: get${Other}(${Other}.nan)))
|
|
}
|
|
|
|
% if Other == 'Float80':
|
|
#endif
|
|
% end
|
|
|
|
% end # for in all_floating_point_types (Other)
|
|
% end # for in all_integer_types (Self)
|
|
|
|
runAllTests()
|