Files
swift-mirror/test/stdlib/Inputs/FixedPointConversion.swift.gyb
Daniel Rodríguez Troitiño 4dcf60ca0f [test] Fix FixedPointConversion tests (#23220)
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>
2019-03-21 13:58:30 -04:00

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()