Skip to content

Commit

Permalink
Merge pull request #147 from DataDog/ncreated/RUMM-486-run-tests-on-a…
Browse files Browse the repository at this point in the history
…ll-supported-iOS-versions

RUMM-486 Fix span encoding error on iOS versions prior to 13.0
  • Loading branch information
ncreated authored Jun 19, 2020
2 parents eb65038 + b20842c commit 3eb6de6
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 45 deletions.
4 changes: 4 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
61E917D12465423600E6C631 /* DDTracerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E917D02465423600E6C631 /* DDTracerConfiguration.swift */; };
61E917D3246546BF00E6C631 /* DDTracerConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E917D2246546BF00E6C631 /* DDTracerConfigurationTests.swift */; };
61F1A61A2498A51700075390 /* CoreMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F1A6192498A51700075390 /* CoreMocks.swift */; };
61F1A623249B811200075390 /* Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F1A622249B811200075390 /* Encoding.swift */; };
61F8CC092469295500FE2908 /* DatadogConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F8CC082469295500FE2908 /* DatadogConfigurationTests.swift */; };
61FB222D244A21ED00902D19 /* LoggingFeatureMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FB222C244A21ED00902D19 /* LoggingFeatureMocks.swift */; };
61FB2230244E1BE900902D19 /* LoggingFeatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FB222F244E1BE900902D19 /* LoggingFeatureTests.swift */; };
Expand Down Expand Up @@ -386,6 +387,7 @@
61E917D02465423600E6C631 /* DDTracerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDTracerConfiguration.swift; sourceTree = "<group>"; };
61E917D2246546BF00E6C631 /* DDTracerConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDTracerConfigurationTests.swift; sourceTree = "<group>"; };
61F1A6192498A51700075390 /* CoreMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreMocks.swift; sourceTree = "<group>"; };
61F1A622249B811200075390 /* Encoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encoding.swift; sourceTree = "<group>"; };
61F8CC082469295500FE2908 /* DatadogConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogConfigurationTests.swift; sourceTree = "<group>"; };
61FB222C244A21ED00902D19 /* LoggingFeatureMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingFeatureMocks.swift; sourceTree = "<group>"; };
61FB222F244E1BE900902D19 /* LoggingFeatureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingFeatureTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -853,6 +855,7 @@
61133C452423990D00786299 /* SwiftExtensions.swift */,
61133C462423990D00786299 /* TestsDirectory.swift */,
61133C472423990D00786299 /* DatadogExtensions.swift */,
61F1A622249B811200075390 /* Encoding.swift */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -1573,6 +1576,7 @@
9E36D92224373EA700BFBDB7 /* SwiftExtensionsTests.swift in Sources */,
61133C652423990D00786299 /* LogBuilderTests.swift in Sources */,
61F8CC092469295500FE2908 /* DatadogConfigurationTests.swift in Sources */,
61F1A623249B811200075390 /* Encoding.swift in Sources */,
61133C642423990D00786299 /* LoggerTests.swift in Sources */,
61E45BE5245196EA00F2C652 /* SpanFileOutputTests.swift in Sources */,
61133C4E2423990D00786299 /* UIKitMocks.swift in Sources */,
Expand Down
11 changes: 11 additions & 0 deletions Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Datadog.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@
BlueprintName = "DatadogTests"
ReferencedContainer = "container:Datadog.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "URLSessionSwizzlerTests/test_dataTask_urlCompletion_alwaysIntercept()">
</Test>
<Test
Identifier = "URLSessionSwizzlerTests_CustomDelegate/test_dataTask_urlCompletion_alwaysIntercept()">
</Test>
<Test
Identifier = "URLSessionSwizzlerTests_DefaultConfig/test_dataTask_urlCompletion_alwaysIntercept()">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
</TestAction>
Expand Down
28 changes: 27 additions & 1 deletion Sources/Datadog/Core/Utils/EncodableValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,33 @@ internal struct JSONStringEncodableValue: Encodable {
// Switch to encode `url.absoluteString` directly - see the comment in `EncodableValue`
try urlValue.absoluteString.encode(to: encoder)
} else {
let jsonData = try jsonEncoder.encode(encodable)
let jsonData: Data

if #available(iOS 13.0, *) {
jsonData = try jsonEncoder.encode(encodable)
} else {
// Prior to `iOS13.0` the `JSONEncoder` is unable to encode primitive values - it expects them to be
// wrapped inside top-level JSON object (array or dictionary). Reference: https://bugs.swift.org/browse/SR-6163
//
// As a workaround, we serialize the `encodable` as a JSON array and then remove `[` and `]` bytes from serialized data.
let temporaryJsonArrayData = try jsonEncoder.encode([encodable])

let subdataStartIndex = temporaryJsonArrayData.startIndex.advanced(by: 1)
let subdataEndIndex = temporaryJsonArrayData.endIndex.advanced(by: -1)

guard subdataStartIndex < subdataEndIndex else {
// This error should never be thrown, as the `temporaryJsonArrayData` will always contain at
// least two bytes standing for `[` and `]`. This check is just for sanity.
let encodingContext = EncodingError.Context(
codingPath: encoder.codingPath,
debugDescription: "Cannot safely encode value within a temporary array container."
)
throw EncodingError.invalidValue(encodable.value, encodingContext)
}

jsonData = temporaryJsonArrayData.subdata(in: subdataStartIndex..<subdataEndIndex)
}

if let stringValue = String(data: jsonData, encoding: .utf8) {
try stringValue.encode(to: encoder)
}
Expand Down
17 changes: 10 additions & 7 deletions Tests/DatadogIntegrationTests/TracingIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,18 @@ class TracingIntegrationTests: IntegrationTests {
)
)

try matcher.meta.networkAvailableInterfaces().split(separator: "+").forEach { interface in
XCTAssertTrue(
SpanMatcher.allowedNetworkAvailableInterfacesValues.contains(String(interface))
)
if #available(iOS 12.0, *) { // The `iOS11NetworkConnectionInfoProvider` doesn't provide those info
try matcher.meta.networkAvailableInterfaces().split(separator: "+").forEach { interface in
XCTAssertTrue(
SpanMatcher.allowedNetworkAvailableInterfacesValues.contains(String(interface))
)
}

XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionSupportsIPv4()))
XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionSupportsIPv6()))
XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionIsExpensive()))
}

XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionSupportsIPv4()))
XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionSupportsIPv6()))
XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionIsExpensive()))
if #available(iOS 13.0, *) {
XCTAssertTrue(["0", "1"].contains(try matcher.meta.networkConnectionIsConstrained()))
}
Expand Down
44 changes: 28 additions & 16 deletions Tests/DatadogTests/Datadog/Core/Utils/EncodableValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,22 @@ class EncodableValueTests: XCTestCase {
let encoder = JSONEncoder()

XCTAssertEqual(
try encoder.encode(EncodableValue("string")).utf8String,
#""string""#
try encoder.encode(EncodingContainer(EncodableValue("string"))).utf8String,
#"{"value":"string"}"#
)
XCTAssertEqual(
try encoder.encode(EncodableValue(123)).utf8String,
#"123"#
try encoder.encode(EncodingContainer(EncodableValue(123))).utf8String,
#"{"value":123}"#
)
XCTAssertEqual(
try encoder.encode(EncodableValue(["a", "b", "c"])).utf8String,
#"["a","b","c"]"#
)
XCTAssertEqual(
try encoder.encode(EncodableValue(URL(string: "https://example.com/image.png")!)).utf8String,
#""https:\/\/example.com\/image.png""#
try encoder.encode(
EncodingContainer(EncodableValue(URL(string: "https://example.com/image.png")!))
).utf8String,
#"{"value":"https:\/\/example.com\/image.png"}"#
)
struct Foo: Encodable {
let bar = "bar_"
Expand All @@ -43,30 +45,40 @@ class JSONStringEncodableValueTests: XCTestCase {
let encoder = JSONEncoder()

XCTAssertEqual(
try encoder.encode(JSONStringEncodableValue("string", encodedUsing: JSONEncoder())).utf8String,
#""string""#
try encoder.encode(
EncodingContainer(JSONStringEncodableValue("string", encodedUsing: JSONEncoder()))
).utf8String,
#"{"value":"string"}"#
)
XCTAssertEqual(
try encoder.encode(JSONStringEncodableValue(123, encodedUsing: JSONEncoder())).utf8String,
#""123""#
try encoder.encode(
EncodingContainer(JSONStringEncodableValue(123, encodedUsing: JSONEncoder()))
).utf8String,
#"{"value":"123"}"#
)
XCTAssertEqual(
try encoder.encode(JSONStringEncodableValue(["a", "b", "c"], encodedUsing: JSONEncoder())).utf8String,
#""[\"a\",\"b\",\"c\"]""#
try encoder.encode(
EncodingContainer(JSONStringEncodableValue(["a", "b", "c"], encodedUsing: JSONEncoder()))
).utf8String,
#"{"value":"[\"a\",\"b\",\"c\"]"}"#
)
XCTAssertEqual(
try encoder.encode(
JSONStringEncodableValue(URL(string: "https://example.com/image.png")!, encodedUsing: JSONEncoder())
EncodingContainer(
JSONStringEncodableValue(URL(string: "https://example.com/image.png")!, encodedUsing: JSONEncoder())
)
).utf8String,
#""https:\/\/example.com\/image.png""#
#"{"value":"https:\/\/example.com\/image.png"}"#
)
struct Foo: Encodable {
let bar = "bar_"
let bizz = "bizz_"
}
XCTAssertEqual(
try encoder.encode(JSONStringEncodableValue(Foo(), encodedUsing: JSONEncoder())).utf8String,
#""{\"bar\":\"bar_\",\"bizz\":\"bizz_\"}""#
try encoder.encode(
EncodingContainer(JSONStringEncodableValue(Foo(), encodedUsing: JSONEncoder()))
).utf8String,
#"{"value":"{\"bar\":\"bar_\",\"bizz\":\"bizz_\"}"}"#
)
}

Expand Down
24 changes: 9 additions & 15 deletions Tests/DatadogTests/Datadog/Core/Utils/JSONEncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,22 @@ class JSONEncoderTests: XCTestCase {
private let jsonEncoder = JSONEncoder.default()

func testDateEncoding() throws {
/// Prior to `iOS13.0` `JSONEncoder` supports only object or array as the root type, hence we can't encode `Date` directly.
struct Container: Encodable {
let date: Date = .mockDecember15th2019At10AMUTC(addingTimeInterval: 0.123)
}

let encodedDate = try jsonEncoder.encode(Container())
let encodedDate = try jsonEncoder.encode(
EncodingContainer(Date.mockDecember15th2019At10AMUTC(addingTimeInterval: 0.123))
)

XCTAssertEqual(encodedDate.utf8String, #"{"date":"2019-12-15T10:00:00.123Z"}"#)
XCTAssertEqual(encodedDate.utf8String, #"{"value":"2019-12-15T10:00:00.123Z"}"#)
}

func testURLEncoding() throws {
/// Prior to `iOS13.0` `JSONEncoder` supports only object or array as the root type, hence we can't encode `URL` directly.
struct Container: Encodable {
let url = URL(string: "https://example.com/foo")!
}

let encodedURL = try jsonEncoder.encode(Container())
let encodedURL = try jsonEncoder.encode(
EncodingContainer(URL(string: "https://example.com/foo")!)
)

if #available(iOS 13.0, OSX 10.15, *) {
XCTAssertEqual(encodedURL.utf8String, #"{"url":"https://example.com/foo"}"#)
XCTAssertEqual(encodedURL.utf8String, #"{"value":"https://example.com/foo"}"#)
} else {
XCTAssertEqual(encodedURL.utf8String, #"{"url":"https:\/\/example.com\/foo"}"#)
XCTAssertEqual(encodedURL.utf8String, #"{"value":"https:\/\/example.com\/foo"}"#)
}
}
}
6 changes: 0 additions & 6 deletions Tests/DatadogTests/Helpers/DatadogExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ extension Date {
}
}

extension EncodableValue: Equatable {
public static func == (lhs: EncodableValue, rhs: EncodableValue) -> Bool {
return String(describing: lhs) == String(describing: rhs)
}
}

extension File {
func makeReadonly() throws {
try FileManager.default.setAttributes([.immutable: true], ofItemAtPath: url.path)
Expand Down
25 changes: 25 additions & 0 deletions Tests/DatadogTests/Helpers/Encoding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-2020 Datadog, Inc.
*/

@testable import Datadog

extension EncodableValue: Equatable {
public static func == (lhs: EncodableValue, rhs: EncodableValue) -> Bool {
return String(describing: lhs) == String(describing: rhs)
}
}

/// Prior to `iOS13.0`, the `JSONEncoder` supports only object or array as the root type.
/// Hence we can't test encoding `Encodable` values directly and we need as support of this `EncodingContainer` container.
///
/// Reference: https://bugs.swift.org/browse/SR-6163
struct EncodingContainer<Value: Encodable>: Encodable {
let value: Value

init(_ value: Value) {
self.value = value
}
}

0 comments on commit 3eb6de6

Please sign in to comment.