|
9 | 9 | // RUN: %target-run-simple-swift
|
10 | 10 | // REQUIRES: executable_test
|
11 | 11 | // REQUIRES: objc_interop
|
12 |
| -// REQUIRES: rdar49161053 |
13 | 12 |
|
14 | 13 | import Swift
|
15 | 14 | import Foundation
|
@@ -231,11 +230,71 @@ class TestJSONEncoder : TestJSONEncoderSuper {
|
231 | 230 |
|
232 | 231 | // MARK: - Date Strategy Tests
|
233 | 232 | func testEncodingDate() {
|
| 233 | + |
| 234 | + func formattedLength(of value: Double) -> Int { |
| 235 | + let empty = UnsafeMutablePointer<Int8>.allocate(capacity: 0) |
| 236 | + defer { empty.deallocate() } |
| 237 | + let length = snprintf(ptr: empty, 0, "%0.*g", DBL_DECIMAL_DIG, value) |
| 238 | + return Int(length) |
| 239 | + } |
| 240 | + |
| 241 | + // Duplicated to handle a special case |
| 242 | + func localTestRoundTrip<T: Codable & Equatable>(of value: T) { |
| 243 | + var payload: Data! = nil |
| 244 | + do { |
| 245 | + let encoder = JSONEncoder() |
| 246 | + payload = try encoder.encode(value) |
| 247 | + } catch { |
| 248 | + expectUnreachable("Failed to encode \(T.self) to JSON: \(error)") |
| 249 | + } |
| 250 | + |
| 251 | + do { |
| 252 | + let decoder = JSONDecoder() |
| 253 | + let decoded = try decoder.decode(T.self, from: payload) |
| 254 | + |
| 255 | + /// `snprintf`'s `%g`, which `JSONSerialization` uses internally for double values, does not respect |
| 256 | + /// our precision requests in every case. This bug effects Darwin, FreeBSD, and Linux currently |
| 257 | + /// causing this test (which uses the current time) to fail occasionally. |
| 258 | + let evalEdgeCase: (Date, Date) -> () = { decodedDate, expectedDate in |
| 259 | + if formattedLength(of: decodedDate.timeIntervalSinceReferenceDate) > DBL_DECIMAL_DIG + 2 { |
| 260 | + let adjustedTimeIntervalSinceReferenceDate: (Date) -> Double = { |
| 261 | + let adjustment = pow(10, Double(DBL_DECIMAL_DIG)) |
| 262 | + return Double(floor(adjustment * $0.timeIntervalSinceReferenceDate) / adjustment) |
| 263 | + } |
| 264 | + |
| 265 | + let decodedAprox = adjustedTimeIntervalSinceReferenceDate(decodedDate) |
| 266 | + let valueAprox = adjustedTimeIntervalSinceReferenceDate(expectedDate) |
| 267 | + expectEqual(decodedAprox, valueAprox, "\(T.self) did not round-trip to an equal value after DBL_DECIMAL_DIG adjustment \(decodedAprox) != \(valueAprox).") |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + if let decodedDate = (decoded as? TopLevelWrapper<Date>)?.value, |
| 272 | + let expectedDate = (value as? TopLevelWrapper<Date>)?.value { |
| 273 | + evalEdgeCase(decodedDate, expectedDate) |
| 274 | + return |
| 275 | + } |
| 276 | + |
| 277 | + if let decodedDate = (decoded as? OptionalTopLevelWrapper<Date>)?.value, |
| 278 | + let expectedDate = (value as? OptionalTopLevelWrapper<Date>)?.value { |
| 279 | + evalEdgeCase(decodedDate, expectedDate) |
| 280 | + return |
| 281 | + } |
| 282 | + |
| 283 | + expectEqual(decoded, value, "\(T.self) did not round-trip to an equal value.") |
| 284 | + } catch { |
| 285 | + expectUnreachable("Failed to decode \(T.self) from JSON: \(error)") |
| 286 | + } |
| 287 | + } |
| 288 | + |
| 289 | + // Test the above `snprintf` edge case evaluation with a known triggering case |
| 290 | + let knownBadDate = Date(timeIntervalSinceReferenceDate: 0.0021413276231263384) |
| 291 | + localTestRoundTrip(of: TopLevelWrapper(knownBadDate)) |
| 292 | + |
234 | 293 | // We can't encode a top-level Date, so it'll be wrapped in a dictionary.
|
235 |
| - _testRoundTrip(of: TopLevelWrapper(Date())) |
| 294 | + localTestRoundTrip(of: TopLevelWrapper(Date())) |
236 | 295 |
|
237 | 296 | // Optional dates should encode the same way.
|
238 |
| - _testRoundTrip(of: OptionalTopLevelWrapper(Date())) |
| 297 | + localTestRoundTrip(of: OptionalTopLevelWrapper(Date())) |
239 | 298 | }
|
240 | 299 |
|
241 | 300 | func testEncodingDateSecondsSince1970() {
|
|
0 commit comments