From 99ab25a5098a2ee8cd101f4c9858296f1bdf7351 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 31 Aug 2021 12:02:20 +0200 Subject: [PATCH 01/80] RUMM-1566 Always link `DDCrashReporting` as static library as it has transitive dependency that includes statically linked binary: PLCrashReporter/iOS Framework/CrashReporter.framework --- DatadogSDKCrashReporting.podspec | 1 + DatadogSDKCrashReporting.podspec.src | 1 + 2 files changed, 2 insertions(+) diff --git a/DatadogSDKCrashReporting.podspec b/DatadogSDKCrashReporting.podspec index a5edf1ef4e..f8b919a66e 100644 --- a/DatadogSDKCrashReporting.podspec +++ b/DatadogSDKCrashReporting.podspec @@ -17,6 +17,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' s.source = { :git => 'https://github.com/DataDog/dd-sdk-ios.git', :tag => s.version.to_s } + s.static_framework = true s.source_files = "Sources/DatadogCrashReporting/**/*.swift" s.dependency 'DatadogSDK', '1.7.0-beta4' diff --git a/DatadogSDKCrashReporting.podspec.src b/DatadogSDKCrashReporting.podspec.src index cd9fde6dab..7fcd894895 100644 --- a/DatadogSDKCrashReporting.podspec.src +++ b/DatadogSDKCrashReporting.podspec.src @@ -17,6 +17,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '11.0' s.source = { :git => 'https://github.com/DataDog/dd-sdk-ios.git', :tag => s.version.to_s } + s.static_framework = true s.source_files = "Sources/DatadogCrashReporting/**/*.swift" s.dependency 'DatadogSDK', '__DATADOG_VERSION__' From 420e7d7bde3f67c7fe8705a92a8bb80532e7a45e Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 31 Aug 2021 14:20:55 +0200 Subject: [PATCH 02/80] RUMM-1566 Remove `static_framework` workaround from CPProject's Podfile --- .../cocoapods/CPProject.xcodeproj/project.pbxproj | 4 +++- .../xcshareddata/xcschemes/CPProject.xcscheme | 2 +- dependency-manager-tests/cocoapods/Podfile.src | 14 -------------- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj index 2b630c4aff..d1ec7f0529 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj @@ -256,7 +256,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1140; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = Datadog; TargetAttributes = { 61C3638E2436318E00C4D4E6 = { @@ -546,6 +546,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -607,6 +608,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme index 4cef192bd3..14d2b3f39b 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 31 Aug 2021 19:50:55 +0200 Subject: [PATCH 03/80] RUMM-1420 Set `internal(set) accessor to readonly additionalProperties --- .../Swift/JSONToSwiftTypeTransformer.swift | 28 +++- .../Input/Swift/SwiftType.swift | 8 +- .../Output/Printing/ObjcInteropPrinter.swift | 10 +- .../Output/Printing/SwiftPrinter.swift | 7 +- .../JSONToSwiftTypeTransformerTests.swift | 24 +-- .../Output/ObjcInteropPrinterTests.swift | 144 +++++++++--------- .../Output/SwiftPrinterTests.swift | 26 ++-- .../RUM/RUMSwiftTypeTransformerTests.swift | 36 ++--- 8 files changed, 153 insertions(+), 130 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift index 832bb4a2f9..c0a2a4ee2b 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift @@ -78,6 +78,7 @@ internal class JSONToSwiftTypeTransformer { // In generated encoding code, this dictionary is erased but its keys and values are used as dynamic // properties encoded in JSON. let additionalPropertyName = jsonObject.name + "Info" + let mutability: SwiftStruct.Property.Mutability = additionalProperties.isReadOnly ? .mutableInternally : .mutable var `struct` = try transformJSONToStruct(jsonObject) `struct`.properties.append( SwiftStruct.Property( @@ -87,7 +88,7 @@ internal class JSONToSwiftTypeTransformer { value: SwiftPrimitive() ), isOptional: false, - isMutable: !additionalProperties.isReadOnly, + mutability: mutability, defaultValue: nil, codingKey: .dynamic ) @@ -125,12 +126,13 @@ internal class JSONToSwiftTypeTransformer { } return try objectProperties.map { jsonProperty in + let mutability: SwiftStruct.Property.Mutability = jsonProperty.isReadOnly ? .immutable : .mutable return SwiftStruct.Property( name: jsonProperty.name, comment: jsonProperty.comment, type: try transformJSONToAnyType(jsonProperty.type), isOptional: !jsonProperty.isRequired, - isMutable: !jsonProperty.isReadOnly, + mutability: mutability, defaultValue: try readDefaultValue(for: jsonProperty), codingKey: .static(value: jsonProperty.name) ) @@ -175,7 +177,7 @@ internal class JSONToSwiftTypeTransformer { `struct`.properties = `struct`.properties.map { property in var property = property - property.isMutable = property.isMutable || hasTransitiveMutableProperty(type: property.type) + property.mutability = transitiveMutability(property: property) if let nestedStruct = property.type as? SwiftStruct { property.type = resolveTransitiveMutableProperties(in: nestedStruct) @@ -188,16 +190,26 @@ internal class JSONToSwiftTypeTransformer { } /// Returns `true` if the given `SwiftType` contains a mutable property (`var`) or any of its nested types does. - private func hasTransitiveMutableProperty(type: SwiftType) -> Bool { + private func transitiveMutableProperty(type: SwiftType) -> SwiftStruct.Property.Mutability { switch type { case let array as SwiftArray: - return hasTransitiveMutableProperty(type: array.element) + return transitiveMutableProperty(type: array.element) case let `struct` as SwiftStruct: - return `struct`.properties.contains { property in - property.isMutable || hasTransitiveMutableProperty(type: property.type) + return `struct`.properties.reduce(.immutable) { + let transitiveMutability = transitiveMutability(property: $1) + return transitiveMutability.rawValue > $0.rawValue ? transitiveMutability : $0 } default: - return false + return .immutable } } + + /// Returns `true` if the given `SwiftStruct.Property` contains a mutable property (`var`) or any of its nested types does. + private func transitiveMutability(property: SwiftStruct.Property) -> SwiftStruct.Property.Mutability { + let transitiveMutability = transitiveMutableProperty(type: property.type) + if property.mutability.rawValue > transitiveMutability.rawValue { + return property.mutability + } + return transitiveMutability + } } diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift index b4c3ca0222..bba103d9f7 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift @@ -54,6 +54,12 @@ internal struct SwiftEnum: SwiftType { internal struct SwiftStruct: SwiftType { struct Property: SwiftType { + enum Mutability: Int { + case immutable + case mutableInternally + case mutable + } + enum CodingKey { /// Static coding key with fixed value. case `static`(value: String) @@ -72,7 +78,7 @@ internal struct SwiftStruct: SwiftType { var comment: String? var type: SwiftType var isOptional: Bool - var isMutable: Bool + var mutability: Mutability var defaultValue: SwiftPropertyDefaultValue? var codingKey: CodingKey } diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift index efb8746b4f..a5dd41e999 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift @@ -241,7 +241,7 @@ internal class ObjcInteropPrinter: BasePrinter { let objcPropertyName = swiftProperty.name let objcEnumName = objcTypeNamesPrefix + nestedObjcEnum.objcTypeName - if swiftProperty.isMutable { + if swiftProperty.mutability == .mutable { writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") indentRight() writeLine("set { root.swiftModel.\(propertyWrapper.keyPath) = newValue.toSwift }") @@ -273,7 +273,7 @@ internal class ObjcInteropPrinter: BasePrinter { let objcPropertyOptionality = swiftProperty.isOptional ? "?" : "" let objcEnumName = objcTypeNamesPrefix + nestedObjcEnumArray.objcTypeName - guard swiftProperty.isMutable == false else { + if swiftProperty.mutability == .mutable { throw Exception.unimplemented("Generating setter for `ObjcInteropEnumArray` is not supported: \(swiftProperty.type).") } @@ -298,7 +298,7 @@ internal class ObjcInteropPrinter: BasePrinter { let objcPropertyOptionality = swiftProperty.isOptional ? "?" : "" let objcClassName = objcTypeNamesPrefix + nestedObjcClass.objcTypeName - guard swiftProperty.isMutable == false else { + if swiftProperty.mutability == .mutable { throw Exception.unimplemented("Generating setter for `ObjcInteropNestedClass` is not supported: \(swiftProperty.type).") } @@ -313,7 +313,7 @@ internal class ObjcInteropPrinter: BasePrinter { let swiftProperty = propertyWrapper.bridgedSwiftProperty if let swiftDictionary = swiftProperty.type as? SwiftDictionary, swiftDictionary.value is SwiftPrimitive { - guard !swiftProperty.isMutable else { + if swiftProperty.mutability == .mutable { throw Exception.unimplemented( "Generating ObjcInterop for mutable `[Swift: Codable]` is not supported." ) @@ -327,7 +327,7 @@ internal class ObjcInteropPrinter: BasePrinter { asObjcCast + objcPropertyOptionality } ?? "" - if swiftProperty.isMutable { + if swiftProperty.mutability == .mutable { // Generate getter and setter for the managed value, e.g.: // ``` // @objc public var propertyX: String? { diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift index e87d03b7aa..4f887ddf81 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift @@ -53,7 +53,12 @@ public class SwiftPrinter: BasePrinter { private func printPropertiesList(_ properties: [SwiftStruct.Property]) throws { try properties.enumerated().forEach { index, property in let accessLevel = "public" - let kind = property.isMutable ? "var" : "let" + let kind: String + switch property.mutability { + case .mutable: kind = "var" + case .mutableInternally: kind = "internal(set) var" + default: kind = "let" + } let name = property.name let type = try typeDeclaration(property.type) let optionality = property.isOptional ? "?" : "" diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift index e63e78baba..6488ce2374 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift @@ -87,7 +87,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property1`.", type: SwiftPrimitive(), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property1") ), @@ -96,7 +96,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property2`.", type: SwiftPrimitive(), isOptional: false, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ) @@ -104,7 +104,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: true, // should be mutable as at least one of the `Bar's` properties is mutable + mutability: .mutable, // should be mutable as at least one of the `Bar's` properties is mutable defaultValue: nil, codingKey: .static(value: "bar") ), @@ -123,7 +123,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: SwiftEnum.Case(label: "case2", rawValue: .string(value: "case2")), codingKey: .static(value: "property1") ), @@ -144,7 +144,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { ) ), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ) @@ -193,7 +193,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { comment: "Description of a property with nested additional Int properties.", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "propertyWithAdditionalIntProperties") ) @@ -251,7 +251,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .mutableInternally, defaultValue: nil, codingKey: .dynamic ) @@ -259,7 +259,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .mutableInternally, defaultValue: nil, codingKey: .static(value: "propertyWithAdditionalAnyProperties") ) @@ -323,7 +323,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { comment: "Description of Foo.bar's `bazz`.", type: SwiftPrimitive(), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "bazz") ), @@ -334,7 +334,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .mutableInternally, defaultValue: nil, codingKey: .dynamic ), @@ -342,7 +342,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .mutableInternally, defaultValue: nil, codingKey: .static(value: "bar") ) @@ -423,7 +423,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "propertyWithAdditionalProperties") ) diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift index 9120e965ef..1da407b898 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift @@ -35,25 +35,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableString", type: SwiftPrimitive(), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableString", type: SwiftPrimitive(), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -118,25 +118,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableInt", type: SwiftPrimitive(), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableInt", type: SwiftPrimitive(), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableInt", type: SwiftPrimitive(), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableInt", type: SwiftPrimitive(), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -201,25 +201,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableInt64", type: SwiftPrimitive(), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableInt64", type: SwiftPrimitive(), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableInt", type: SwiftPrimitive(), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableInt64", type: SwiftPrimitive(), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -284,25 +284,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableDouble", type: SwiftPrimitive(), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableDouble", type: SwiftPrimitive(), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableDouble", type: SwiftPrimitive(), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableDouble", type: SwiftPrimitive(), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -367,25 +367,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableBool", type: SwiftPrimitive(), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableBool", type: SwiftPrimitive(), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableBool", type: SwiftPrimitive(), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableBool", type: SwiftPrimitive(), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -463,25 +463,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableEnum", type: mockEnumeration(named: "Enumeration1"), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableEnum", type: mockEnumeration(named: "Enumeration2"), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableEnum", type: mockEnumeration(named: "Enumeration3"), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableEnum", type: mockEnumeration(named: "Enumeration4"), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -670,25 +670,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableStrings", type: SwiftArray(element: SwiftPrimitive()), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableStrings", type: SwiftArray(element: SwiftPrimitive()), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableStrings", type: SwiftArray(element: SwiftPrimitive()), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableStrings", type: SwiftArray(element: SwiftPrimitive()), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -753,25 +753,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableInt64s", type: SwiftArray(element: SwiftPrimitive()), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableInt64s", type: SwiftArray(element: SwiftPrimitive()), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableInt64s", type: SwiftArray(element: SwiftPrimitive()), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableInt64s", type: SwiftArray(element: SwiftPrimitive()), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -849,13 +849,13 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableEnums", type: SwiftArray(element: mockEnumeration(named: "Options1")), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableEnums", type: SwiftArray(element: mockEnumeration(named: "Options2")), isOptional: true, - isMutable: false + mutability: .immutable ), ], conformance: [] @@ -969,7 +969,7 @@ final class ObjcInteropPrinterTests: XCTestCase { comment: nil, type: SwiftPrimitive(), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property") ) @@ -986,13 +986,13 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableStructs", type: SwiftArray(element: mockStruct(named: "Bar")), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableStructs", type: SwiftArray(element: mockStruct(named: "Bizz")), isOptional: true, - isMutable: false + mutability: .immutable ), ], conformance: [] @@ -1081,25 +1081,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableStrings", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableStrings", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableStrings", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableStrings", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -1164,25 +1164,25 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableInt64s", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableInt64s", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: false + mutability: .immutable ), .mock( propertyName: "mutableInt64s", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalMutableInt64s", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -1247,13 +1247,13 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "immutableCodables", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalImmutableCodables", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: false + mutability: .immutable ), ], conformance: [] @@ -1308,15 +1308,15 @@ final class ObjcInteropPrinterTests: XCTestCase { name: "MutableBar", comment: nil, properties: [ - .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, isMutable: false), - .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, isMutable: false), - .mock(propertyName: "mutableString", type: SwiftPrimitive(), isOptional: false, isMutable: true), - .mock(propertyName: "optionalMutableString", type: SwiftPrimitive(), isOptional: true, isMutable: true), + .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, mutability: .immutable), + .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, mutability: .immutable), + .mock(propertyName: "mutableString", type: SwiftPrimitive(), isOptional: false, mutability: .mutable), + .mock(propertyName: "optionalMutableString", type: SwiftPrimitive(), isOptional: true, mutability: .mutable), ], conformance: [] ), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "immutableBar", @@ -1324,13 +1324,13 @@ final class ObjcInteropPrinterTests: XCTestCase { name: "ImmutableBar", comment: nil, properties: [ - .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, isMutable: false), - .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, isMutable: false), + .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, mutability: .immutable), + .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, mutability: .immutable), ], conformance: [] ), isOptional: false, - isMutable: false + mutability: .immutable ), .mock( propertyName: "optionalMutableBar", @@ -1338,13 +1338,13 @@ final class ObjcInteropPrinterTests: XCTestCase { name: "OptionalMutableBar", comment: nil, properties: [ - .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, isMutable: false), - .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, isMutable: false), + .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, mutability: .immutable), + .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, mutability: .immutable), ], conformance: [] ), isOptional: true, - isMutable: true + mutability: .mutable ), .mock( propertyName: "optionalImmutableBar", @@ -1352,13 +1352,13 @@ final class ObjcInteropPrinterTests: XCTestCase { name: "OptionalImmutableBar", comment: nil, properties: [ - .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, isMutable: false), - .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, isMutable: false), + .mock(propertyName: "immutableString", type: SwiftPrimitive(), isOptional: false, mutability: .immutable), + .mock(propertyName: "optionalImmutableString", type: SwiftPrimitive(), isOptional: true, mutability: .immutable), ], conformance: [] ), isOptional: true, - isMutable: false + mutability: .immutable ), ], conformance: [] @@ -1542,13 +1542,13 @@ final class ObjcInteropPrinterTests: XCTestCase { conformance: [] ), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] ), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -1648,19 +1648,19 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "sharedStruct", type: SwiftTypeReference(referencedTypeName: "SharedStruct"), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "sharedEnumeration", type: SwiftTypeReference(referencedTypeName: "SharedEnum"), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] ), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -1680,19 +1680,19 @@ final class ObjcInteropPrinterTests: XCTestCase { propertyName: "sharedStruct", type: SwiftTypeReference(referencedTypeName: "SharedStruct"), isOptional: false, - isMutable: true + mutability: .mutable ), .mock( propertyName: "sharedEnumeration", type: SwiftTypeReference(referencedTypeName: "SharedEnum"), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] ), isOptional: false, - isMutable: true + mutability: .mutable ), ], conformance: [] @@ -1702,7 +1702,7 @@ final class ObjcInteropPrinterTests: XCTestCase { name: "SharedStruct", comment: nil, properties: [ - .mock(propertyName: "integer", type: SwiftPrimitive(), isOptional: true, isMutable: true) + .mock(propertyName: "integer", type: SwiftPrimitive(), isOptional: true, mutability: .mutable) ], conformance: [] ) @@ -1904,14 +1904,14 @@ extension SwiftStruct.Property { propertyName: String, type: SwiftType, isOptional: Bool, - isMutable: Bool + mutability: SwiftStruct.Property.Mutability ) -> SwiftStruct.Property { return SwiftStruct.Property( name: propertyName, comment: nil, type: type, isOptional: isOptional, - isMutable: isMutable, + mutability: mutability, defaultValue: nil, codingKey: .static(value: propertyName) ) diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift index 7fb1a81090..2f9c6c3847 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift @@ -25,7 +25,7 @@ final class SwiftPrinterTests: XCTestCase { comment: "Description of Bar's `property1`.", type: SwiftPrimitive(), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property1") ), @@ -34,7 +34,7 @@ final class SwiftPrinterTests: XCTestCase { comment: "Description of Bar's `property2`.", type: SwiftPrimitive(), isOptional: false, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ) @@ -42,7 +42,7 @@ final class SwiftPrinterTests: XCTestCase { conformance: [codableProtocol] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "bar") ), @@ -61,7 +61,7 @@ final class SwiftPrinterTests: XCTestCase { conformance: [codableProtocol] ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: SwiftEnum.Case(label: "case2", rawValue: .string(value: "case2")), codingKey: .static(value: "bizz") ), @@ -82,7 +82,7 @@ final class SwiftPrinterTests: XCTestCase { ) ), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "buzz") ), @@ -91,7 +91,7 @@ final class SwiftPrinterTests: XCTestCase { comment: "Description of FooBar's `propertiesByNames`.", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "propertiesByNames") ) @@ -215,7 +215,7 @@ final class SwiftPrinterTests: XCTestCase { comment: nil, type: SwiftPrimitive(), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property_1") ), @@ -226,7 +226,7 @@ final class SwiftPrinterTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property_2") ) @@ -267,7 +267,7 @@ final class SwiftPrinterTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .dynamic ) @@ -331,7 +331,7 @@ final class SwiftPrinterTests: XCTestCase { comment: nil, type: SwiftPrimitive(), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property_1") ), @@ -342,7 +342,7 @@ final class SwiftPrinterTests: XCTestCase { value: SwiftPrimitive() ), isOptional: false, - isMutable: false, + mutability: .mutableInternally, defaultValue: nil, codingKey: .dynamic ), @@ -351,7 +351,7 @@ final class SwiftPrinterTests: XCTestCase { comment: nil, type: SwiftPrimitive(), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property_2") ) @@ -367,7 +367,7 @@ final class SwiftPrinterTests: XCTestCase { public struct Foo: Codable { public let property1: Int - public let context: [String: Codable] + public internal(set) var context: [String: Codable] public var property2: Bool? diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/RUM/RUMSwiftTypeTransformerTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/RUM/RUMSwiftTypeTransformerTests.swift index cbdb6b911a..89ae2a986b 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/RUM/RUMSwiftTypeTransformerTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/RUM/RUMSwiftTypeTransformerTests.swift @@ -25,7 +25,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property1`.", type: SwiftPrimitive(), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property1") ), @@ -34,7 +34,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property2`.", type: SwiftPrimitive(), isOptional: false, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ) @@ -42,7 +42,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "bar") ), @@ -61,7 +61,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: SwiftEnum.Case(label: "case2", rawValue: .string(value: "case2")), codingKey: .static(value: "property1") ), @@ -82,7 +82,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { ) ), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ), @@ -91,7 +91,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Foobar's `propertiesByNames`", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "propertiesByNames") ) @@ -118,7 +118,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property1`.", type: SwiftPrimitive(), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "property1") ), @@ -127,7 +127,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Bar's `property2`.", type: SwiftPrimitive(), isOptional: false, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ) @@ -135,7 +135,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [codableProtocol] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "bar") ), @@ -154,7 +154,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [codableProtocol] ), isOptional: false, - isMutable: false, + mutability: .immutable, defaultValue: SwiftEnum.Case(label: "case2", rawValue: .string(value: "case2")), codingKey: .static(value: "property1") ), @@ -175,7 +175,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { ) ), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "property2") ), @@ -184,7 +184,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: "Description of Foobar's `propertiesByNames`", type: SwiftDictionary(value: SwiftPrimitive()), isOptional: true, - isMutable: true, + mutability: .mutable, defaultValue: nil, codingKey: .static(value: "propertiesByNames") ) @@ -211,7 +211,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "connectivity") ), @@ -225,7 +225,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "usr") ), @@ -239,7 +239,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { conformance: [] ), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "method") ) @@ -259,7 +259,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: nil, type: SwiftTypeReference(referencedTypeName: "RUMConnectivity"), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "connectivity") ), @@ -268,7 +268,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: nil, type: SwiftTypeReference(referencedTypeName: "RUMUser"), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "usr") ), @@ -277,7 +277,7 @@ final class RUMSwiftTypeTransformerTests: XCTestCase { comment: nil, type: SwiftTypeReference(referencedTypeName: "RUMMethod"), isOptional: true, - isMutable: false, + mutability: .immutable, defaultValue: nil, codingKey: .static(value: "method") ) From 7477dbacb47a1e15e5e9b219aa6cab454f716210 Mon Sep 17 00:00:00 2001 From: maxep Date: Tue, 31 Aug 2021 20:39:20 +0200 Subject: [PATCH 04/80] RUMM-1420 generate models --- .../RUM/DataModels/RUMDataModels.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift index 992d922c2e..1eb379d531 100644 --- a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift +++ b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift @@ -20,7 +20,7 @@ public struct RUMViewEvent: RUMDataModel { public let connectivity: RUMConnectivity? /// User provided context - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? /// Start of the event in ms from epoch public let date: Int64 @@ -35,7 +35,7 @@ public struct RUMViewEvent: RUMDataModel { public let type: String = "view" /// User properties - public let usr: RUMUser? + public internal(set) var usr: RUMUser? /// View properties public var view: View @@ -339,7 +339,7 @@ public struct RUMResourceEvent: RUMDataModel { public let connectivity: RUMConnectivity? /// User provided context - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? /// Start of the event in ms from epoch public let date: Int64 @@ -357,7 +357,7 @@ public struct RUMResourceEvent: RUMDataModel { public let type: String = "resource" /// User properties - public let usr: RUMUser? + public internal(set) var usr: RUMUser? /// View properties public var view: View @@ -693,7 +693,7 @@ public struct RUMActionEvent: RUMDataModel { public let connectivity: RUMConnectivity? /// User provided context - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? /// Start of the event in ms from epoch public let date: Int64 @@ -708,7 +708,7 @@ public struct RUMActionEvent: RUMDataModel { public let type: String = "action" /// User properties - public let usr: RUMUser? + public internal(set) var usr: RUMUser? /// View properties public var view: View @@ -932,7 +932,7 @@ public struct RUMErrorEvent: RUMDataModel { public let connectivity: RUMConnectivity? /// User provided context - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? /// Start of the event in ms from epoch public let date: Int64 @@ -950,7 +950,7 @@ public struct RUMErrorEvent: RUMDataModel { public let type: String = "error" /// User properties - public let usr: RUMUser? + public internal(set) var usr: RUMUser? /// View properties public var view: View @@ -1241,7 +1241,7 @@ public struct RUMConnectivity: Codable { /// User provided context public struct RUMEventAttributes: Codable { - public let contextInfo: [String: Codable] + public internal(set) var contextInfo: [String: Codable] struct DynamicCodingKey: CodingKey { var stringValue: String @@ -1287,7 +1287,7 @@ public struct RUMUser: Codable { /// Name of the user public let name: String? - public let usrInfo: [String: Codable] + public internal(set) var usrInfo: [String: Codable] enum StaticCodingKeys: String, CodingKey { case email = "email" From da49d1b1a0a89ff0d63159becc8f1efcc8ce0de4 Mon Sep 17 00:00:00 2001 From: maxep Date: Wed, 1 Sep 2021 09:56:19 +0200 Subject: [PATCH 05/80] RUMM-1420 Fix name conflicts --- .../Swift/JSONToSwiftTypeTransformer.swift | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift index c0a2a4ee2b..070e6ffee5 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift @@ -177,7 +177,7 @@ internal class JSONToSwiftTypeTransformer { `struct`.properties = `struct`.properties.map { property in var property = property - property.mutability = transitiveMutability(property: property) + property.mutability = transitiveMutability(of: property) if let nestedStruct = property.type as? SwiftStruct { property.type = resolveTransitiveMutableProperties(in: nestedStruct) @@ -189,27 +189,27 @@ internal class JSONToSwiftTypeTransformer { return `struct` } + /// Returns `true` if the given `SwiftStruct.Property` contains a mutable property (`var`) or any of its nested types does. + private func transitiveMutability(of property: SwiftStruct.Property) -> SwiftStruct.Property.Mutability { + let innerMutability = transitiveMutableProperty(of: property.type) + if property.mutability.rawValue > innerMutability.rawValue { + return property.mutability + } + return innerMutability + } + /// Returns `true` if the given `SwiftType` contains a mutable property (`var`) or any of its nested types does. - private func transitiveMutableProperty(type: SwiftType) -> SwiftStruct.Property.Mutability { + private func transitiveMutableProperty(of type: SwiftType) -> SwiftStruct.Property.Mutability { switch type { case let array as SwiftArray: - return transitiveMutableProperty(type: array.element) + return transitiveMutableProperty(of: array.element) case let `struct` as SwiftStruct: - return `struct`.properties.reduce(.immutable) { - let transitiveMutability = transitiveMutability(property: $1) - return transitiveMutability.rawValue > $0.rawValue ? transitiveMutability : $0 + return `struct`.properties.reduce(.immutable) { max, property in + let innerMutability = transitiveMutability(of: property) + return innerMutability.rawValue > max.rawValue ? innerMutability : max } default: return .immutable } } - - /// Returns `true` if the given `SwiftStruct.Property` contains a mutable property (`var`) or any of its nested types does. - private func transitiveMutability(property: SwiftStruct.Property) -> SwiftStruct.Property.Mutability { - let transitiveMutability = transitiveMutableProperty(type: property.type) - if property.mutability.rawValue > transitiveMutability.rawValue { - return property.mutability - } - return transitiveMutability - } } From f63ad3fc6a212738d7bbfa135a8c04b8880cf247 Mon Sep 17 00:00:00 2001 From: maxep Date: Wed, 1 Sep 2021 12:01:22 +0200 Subject: [PATCH 06/80] RUMM-1420 Add swift property mutability test --- .../Input/SwiftTypeTests.swift | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/SwiftTypeTests.swift diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/SwiftTypeTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/SwiftTypeTests.swift new file mode 100644 index 0000000000..6ab51d1863 --- /dev/null +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/SwiftTypeTests.swift @@ -0,0 +1,21 @@ +/* +* 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. +*/ + +import XCTest +@testable import RUMModelsGeneratorCore + +final class SwiftTypeTests: XCTestCase { + func testSwiftStructProperty_mutabilityLevelOrder() { + let immutable = SwiftStruct.Property.Mutability.immutable.rawValue + let mutableInternally = SwiftStruct.Property.Mutability.mutableInternally.rawValue + let mutable = SwiftStruct.Property.Mutability.mutable.rawValue + + // The level order of property mutability must always be + // .immutable < .mutableInternally < .mutable + XCTAssertTrue(immutable < mutableInternally) + XCTAssertTrue(mutableInternally < mutable) + } +} From 0e47cdea38b647a14506fb6e330665b841a280f3 Mon Sep 17 00:00:00 2001 From: maxep Date: Wed, 1 Sep 2021 12:03:15 +0200 Subject: [PATCH 07/80] RUMM-1420 Add more context in documentation and function names --- .../Swift/JSONToSwiftTypeTransformer.swift | 40 +++++++++++++------ .../Input/Swift/SwiftType.swift | 9 +++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift index 070e6ffee5..ea4da5f235 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift @@ -74,10 +74,12 @@ internal class JSONToSwiftTypeTransformer { if let additionalProperties = jsonObject.additionalProperties { if additionalProperties.type == .any { // RUMM-1401: if schema declares `additionalProperties: true` or `additionalProperties: {type: object, ...}` - // we model it as a `struct` with nested ` Info: [String: Codable]` dictionary. - // In generated encoding code, this dictionary is erased but its keys and values are used as dynamic + // we model it as a `struct` with nested ` Info: [String: Codable]` + // dictionary. In generated encoding code, this dictionary is erased but its keys and values are used as dynamic // properties encoded in JSON. let additionalPropertyName = jsonObject.name + "Info" + // RUMM-1420: we noticed that `additionalProperties` is used for custom user attributes which need to be + // sanitized by the SDK, hence it's very practical for us to generate `.mutableInternally` modifier for those. let mutability: SwiftStruct.Property.Mutability = additionalProperties.isReadOnly ? .mutableInternally : .mutable var `struct` = try transformJSONToStruct(jsonObject) `struct`.properties.append( @@ -189,27 +191,41 @@ internal class JSONToSwiftTypeTransformer { return `struct` } - /// Returns `true` if the given `SwiftStruct.Property` contains a mutable property (`var`) or any of its nested types does. + /// Returns the mutability level of the given `SwiftStruct.Property` by checking the mutability levels of its element or nested types. + /// + /// This stands for: _if the child is mutable, its parent must be mutable too_; + /// e.g.: in `foo.bar.property = 2` expression, not only `property` must be mutable, but also its parent `bar` accessor. private func transitiveMutability(of property: SwiftStruct.Property) -> SwiftStruct.Property.Mutability { - let innerMutability = transitiveMutableProperty(of: property.type) - if property.mutability.rawValue > innerMutability.rawValue { - return property.mutability - } - return innerMutability + resolve(parentMutability: property.mutability, childMutability: transitiveMutableProperty(of: property.type)) } - /// Returns `true` if the given `SwiftType` contains a mutable property (`var`) or any of its nested types does. + /// Returns the mutability level of the given `SwiftType` by checking the mutability levels of its element or nested types. + /// + /// This stands for: _if the child is mutable, its parent must be mutable too_; + /// e.g.: in `foo.bar.property = 2` expression, not only `property` must be mutable, but also its parent `bar` accessor. private func transitiveMutableProperty(of type: SwiftType) -> SwiftStruct.Property.Mutability { switch type { case let array as SwiftArray: return transitiveMutableProperty(of: array.element) case let `struct` as SwiftStruct: - return `struct`.properties.reduce(.immutable) { max, property in - let innerMutability = transitiveMutability(of: property) - return innerMutability.rawValue > max.rawValue ? innerMutability : max + // Returns the highest level of mutability of the struct's inner properties + // .immutable < .mutableInternally < .mutable + return `struct`.properties.reduce(.immutable) { + self.resolve(parentMutability: $0, childMutability: transitiveMutability(of: $1)) } default: return .immutable } } + + /// Returns new `Mutability` for parent type given its child `Mutability`. + /// + /// This stands for: _if the child is mutable, its parent must be mutable too_; + /// e.g.: in `foo.bar.property = 2` expression, not only `property` must be mutable, but also its parent `bar` accessor. + private func resolve( + parentMutability: SwiftStruct.Property.Mutability, + childMutability: SwiftStruct.Property.Mutability + ) -> SwiftStruct.Property.Mutability { + return parentMutability.rawValue > childMutability.rawValue ? parentMutability : childMutability + } } diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift index bba103d9f7..d098c6a804 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift @@ -54,10 +54,13 @@ internal struct SwiftEnum: SwiftType { internal struct SwiftStruct: SwiftType { struct Property: SwiftType { + + /// Mutability levels of a property. + /// From the lowest `immutable` to the highest `mutable`. enum Mutability: Int { - case immutable - case mutableInternally - case mutable + case immutable + case mutableInternally + case mutable } enum CodingKey { From 2184386069761e0cef7fd9a6135356764dfff1a6 Mon Sep 17 00:00:00 2001 From: maxep Date: Wed, 1 Sep 2021 12:04:08 +0200 Subject: [PATCH 08/80] RUMM-1420 Use explicit switch cases for property mutability --- .../Output/Printing/ObjcInteropPrinter.swift | 27 ++++++++++--------- .../Output/Printing/SwiftPrinter.swift | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift index a5dd41e999..0774d2d82a 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift @@ -241,19 +241,20 @@ internal class ObjcInteropPrinter: BasePrinter { let objcPropertyName = swiftProperty.name let objcEnumName = objcTypeNamesPrefix + nestedObjcEnum.objcTypeName - if swiftProperty.mutability == .mutable { - writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") - indentRight() - writeLine("set { root.swiftModel.\(propertyWrapper.keyPath) = newValue.toSwift }") - writeLine("get { .init(swift: root.swiftModel.\(propertyWrapper.keyPath)) }") - indentLeft() - writeLine("}") - } else { - writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") - indentRight() - writeLine(".init(swift: root.swiftModel.\(propertyWrapper.keyPath))") - indentLeft() - writeLine("}") + switch swiftProperty.mutability { + case .mutable: + writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") + indentRight() + writeLine("set { root.swiftModel.\(propertyWrapper.keyPath) = newValue.toSwift }") + writeLine("get { .init(swift: root.swiftModel.\(propertyWrapper.keyPath)) }") + indentLeft() + writeLine("}") + case .immutable, .mutableInternally: + writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") + indentRight() + writeLine(".init(swift: root.swiftModel.\(propertyWrapper.keyPath))") + indentLeft() + writeLine("}") } } diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift index 4f887ddf81..ce916e2425 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift @@ -57,7 +57,7 @@ public class SwiftPrinter: BasePrinter { switch property.mutability { case .mutable: kind = "var" case .mutableInternally: kind = "internal(set) var" - default: kind = "let" + case .immutable: kind = "let" } let name = property.name let type = try typeDeclaration(property.type) From ab4e29f7237d2d5f744ae53c583e410fe84ec72c Mon Sep 17 00:00:00 2001 From: maxep Date: Wed, 1 Sep 2021 13:02:22 +0200 Subject: [PATCH 09/80] RUMM-1420 Fix linting errors --- .../Input/Swift/SwiftType.swift | 1 - .../Output/Printing/ObjcInteropPrinter.swift | 26 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift index d098c6a804..e56f71da0a 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift @@ -54,7 +54,6 @@ internal struct SwiftEnum: SwiftType { internal struct SwiftStruct: SwiftType { struct Property: SwiftType { - /// Mutability levels of a property. /// From the lowest `immutable` to the highest `mutable`. enum Mutability: Int { diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift index 0774d2d82a..50de1dacc6 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift @@ -242,19 +242,19 @@ internal class ObjcInteropPrinter: BasePrinter { let objcEnumName = objcTypeNamesPrefix + nestedObjcEnum.objcTypeName switch swiftProperty.mutability { - case .mutable: - writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") - indentRight() - writeLine("set { root.swiftModel.\(propertyWrapper.keyPath) = newValue.toSwift }") - writeLine("get { .init(swift: root.swiftModel.\(propertyWrapper.keyPath)) }") - indentLeft() - writeLine("}") - case .immutable, .mutableInternally: - writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") - indentRight() - writeLine(".init(swift: root.swiftModel.\(propertyWrapper.keyPath))") - indentLeft() - writeLine("}") + case .mutable: + writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") + indentRight() + writeLine("set { root.swiftModel.\(propertyWrapper.keyPath) = newValue.toSwift }") + writeLine("get { .init(swift: root.swiftModel.\(propertyWrapper.keyPath)) }") + indentLeft() + writeLine("}") + case .immutable, .mutableInternally: + writeLine("@objc public var \(objcPropertyName): \(objcEnumName) {") + indentRight() + writeLine(".init(swift: root.swiftModel.\(propertyWrapper.keyPath))") + indentLeft() + writeLine("}") } } From 67838d889fabeac0dae4f09258a83429a99a8b17 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 1 Sep 2021 16:26:33 +0200 Subject: [PATCH 10/80] RUMM-1582 Expose missing `DDRUMView` and `DDRUMAction` APIs to Obj-c --- Sources/DatadogObjc/RUMMonitor+objc.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/DatadogObjc/RUMMonitor+objc.swift b/Sources/DatadogObjc/RUMMonitor+objc.swift index bbe0ccbb21..8324c0c31a 100644 --- a/Sources/DatadogObjc/RUMMonitor+objc.swift +++ b/Sources/DatadogObjc/RUMMonitor+objc.swift @@ -36,6 +36,7 @@ public class DDRUMView: NSObject { /// - Parameters: /// - name: the RUM View name, appearing as `VIEW NAME` in RUM Explorer. /// - attributes: additional attributes to associate with the RUM View. + @objc public init(name: String, attributes: [String: Any]) { swiftView = RUMView( name: name, @@ -71,6 +72,7 @@ public class DDRUMAction: NSObject { /// - Parameters: /// - name: the RUM Action name, appearing as `ACTION NAME` in RUM Explorer. /// - attributes: additional attributes to associate with the RUM Action. + @objc public init(name: String, attributes: [String: Any]) { swiftAction = RUMAction( name: name, From 88b3d4d4bc79def9a76717ded69fad0be3f525bd Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 1 Sep 2021 16:29:24 +0200 Subject: [PATCH 11/80] RUMM-1582 Add smoke tests for public Objc APIs --- Datadog/Datadog.xcodeproj/project.pbxproj | 28 +++++ Sources/DatadogObjc/Datadog+objc.swift | 7 ++ .../ObjcAPITests/DDConfiguration+apiTests.m | 111 ++++++++++++++++++ .../ObjcAPITests/DDDatadog+apiTests.m | 50 ++++++++ .../ObjcAPITests/DDGlobal+apiTests.m | 28 +++++ .../DDNSURLSessionDelegate+apiTests.m | 28 +++++ .../ObjcAPITests/DDRUMMonitor+apiTests.m | 76 ++++++++++++ 7 files changed, 328 insertions(+) create mode 100644 Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m create mode 100644 Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDDatadog+apiTests.m create mode 100644 Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDGlobal+apiTests.m create mode 100644 Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDNSURLSessionDelegate+apiTests.m create mode 100644 Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 43f34b955f..bf490d330b 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -325,6 +325,11 @@ 61B3BD52266128D300A9BEF0 /* LoggerE2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B3BD51266128D300A9BEF0 /* LoggerE2ETests.swift */; }; 61B558CF2469561C001460D3 /* LoggerBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B558CE2469561C001460D3 /* LoggerBuilderTests.swift */; }; 61B558D42469CDD8001460D3 /* TracingUUIDGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B558D32469CDD8001460D3 /* TracingUUIDGeneratorTests.swift */; }; + 61B5E42126DF85C7000B0A5F /* DDRUMMonitor+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5E42026DF85C7000B0A5F /* DDRUMMonitor+apiTests.m */; }; + 61B5E42526DFAFBC000B0A5F /* DDGlobal+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5E42426DFAFBC000B0A5F /* DDGlobal+apiTests.m */; }; + 61B5E42726DFB145000B0A5F /* DDDatadog+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5E42626DFB145000B0A5F /* DDDatadog+apiTests.m */; }; + 61B5E42926DFB60A000B0A5F /* DDConfiguration+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5E42826DFB60A000B0A5F /* DDConfiguration+apiTests.m */; }; + 61B5E42B26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 61B5E42A26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m */; }; 61B6811F25F0EA860015B4AF /* CrashReportingWithLoggingScenarioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B6811E25F0EA860015B4AF /* CrashReportingWithLoggingScenarioTests.swift */; }; 61B6815E25F135890015B4AF /* CrashReportingWithRUMScenarioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B6815D25F135890015B4AF /* CrashReportingWithRUMScenarioTests.swift */; }; 61B7885D25C180CB002675B5 /* DatadogCrashReporting.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61B7885425C180CB002675B5 /* DatadogCrashReporting.framework */; }; @@ -948,6 +953,11 @@ 61B3BD51266128D300A9BEF0 /* LoggerE2ETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerE2ETests.swift; sourceTree = ""; }; 61B558CE2469561C001460D3 /* LoggerBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerBuilderTests.swift; sourceTree = ""; }; 61B558D32469CDD8001460D3 /* TracingUUIDGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingUUIDGeneratorTests.swift; sourceTree = ""; }; + 61B5E42026DF85C7000B0A5F /* DDRUMMonitor+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDRUMMonitor+apiTests.m"; sourceTree = ""; }; + 61B5E42426DFAFBC000B0A5F /* DDGlobal+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDGlobal+apiTests.m"; sourceTree = ""; }; + 61B5E42626DFB145000B0A5F /* DDDatadog+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDDatadog+apiTests.m"; sourceTree = ""; }; + 61B5E42826DFB60A000B0A5F /* DDConfiguration+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDConfiguration+apiTests.m"; sourceTree = ""; }; + 61B5E42A26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDNSURLSessionDelegate+apiTests.m"; sourceTree = ""; }; 61B6811E25F0EA860015B4AF /* CrashReportingWithLoggingScenarioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportingWithLoggingScenarioTests.swift; sourceTree = ""; }; 61B6815D25F135890015B4AF /* CrashReportingWithRUMScenarioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportingWithRUMScenarioTests.swift; sourceTree = ""; }; 61B7885425C180CB002675B5 /* DatadogCrashReporting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DatadogCrashReporting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1550,6 +1560,7 @@ 61133C132423990D00786299 /* DatadogObjc */ = { isa = PBXGroup; children = ( + 61B5E41F26DF857E000B0A5F /* ObjcAPITests */, 61133C142423990D00786299 /* DDDatadogTests.swift */, 61133C162423990D00786299 /* DDConfigurationTests.swift */, 616B6683259CAE3300968EE8 /* DDGlobalTests.swift */, @@ -2605,6 +2616,18 @@ path = Logging; sourceTree = ""; }; + 61B5E41F26DF857E000B0A5F /* ObjcAPITests */ = { + isa = PBXGroup; + children = ( + 61B5E42626DFB145000B0A5F /* DDDatadog+apiTests.m */, + 61B5E42826DFB60A000B0A5F /* DDConfiguration+apiTests.m */, + 61B5E42026DF85C7000B0A5F /* DDRUMMonitor+apiTests.m */, + 61B5E42A26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m */, + 61B5E42426DFAFBC000B0A5F /* DDGlobal+apiTests.m */, + ); + path = ObjcAPITests; + sourceTree = ""; + }; 61B6811D25F0E8480015B4AF /* CrashReporting */ = { isa = PBXGroup; children = ( @@ -3890,6 +3913,7 @@ 61133C672423990D00786299 /* LogConsoleOutputTests.swift in Sources */, 6114FDEC257659E90084E372 /* FeatureDirectoriesMock.swift in Sources */, 61122EE825B1C92500F9C7F5 /* SpanSanitizerTests.swift in Sources */, + 61B5E42526DFAFBC000B0A5F /* DDGlobal+apiTests.m in Sources */, 61FB222D244A21ED00902D19 /* LoggingFeatureMocks.swift in Sources */, 617B954224BF4E7600E6F443 /* RUMMonitorConfigurationTests.swift in Sources */, 614BF37E2670AE9D002379C8 /* AttributesMocks.swift in Sources */, @@ -3978,12 +4002,14 @@ 614B0A5124EBDC8000A2A780 /* RUMConnectivityInfoProviderTests.swift in Sources */, 611F82032563C66100CB9BDB /* UIKitRUMViewsPredicateTests.swift in Sources */, 61133C652423990D00786299 /* LogBuilderTests.swift in Sources */, + 61B5E42B26DFC433000B0A5F /* DDNSURLSessionDelegate+apiTests.m in Sources */, 61F8CC092469295500FE2908 /* DatadogConfigurationBuilderTests.swift in Sources */, 61F1A623249B811200075390 /* Encoding.swift in Sources */, 6114FE3B25768AA90084E372 /* ConsentProviderTests.swift in Sources */, 61133C642423990D00786299 /* LoggerTests.swift in Sources */, 617B953D24BF4D8F00E6F443 /* RUMMonitorTests.swift in Sources */, 61F187FC25FA7DD60022CE9A /* InternalMonitoringFeatureTests.swift in Sources */, + 61B5E42126DF85C7000B0A5F /* DDRUMMonitor+apiTests.m in Sources */, 61786F7724FCDE05009E6BAB /* RUMDebuggingTests.swift in Sources */, 61F9CA712512450B000A5E61 /* RUMAutoInstrumentationTests.swift in Sources */, 6156CB9324DDAA34008CB2B2 /* RUMCurrentContextTests.swift in Sources */, @@ -4004,9 +4030,11 @@ 61F2724925C943C500D54BF8 /* CrashReporterTests.swift in Sources */, 6172472725D673D7007085B3 /* CrashContextTests.swift in Sources */, 61BAD46A26415FCE001886CA /* OTSpanTests.swift in Sources */, + 61B5E42726DFB145000B0A5F /* DDDatadog+apiTests.m in Sources */, 6121627C247D220500AC5D67 /* LoggingForTracingAdapterTests.swift in Sources */, 61133C532423990D00786299 /* MobileDeviceTests.swift in Sources */, 61FF282124B8981D000B3D9B /* RUMEventBuilderTests.swift in Sources */, + 61B5E42926DFB60A000B0A5F /* DDConfiguration+apiTests.m in Sources */, 61345613244756E300E7DA6B /* PerformancePresetTests.swift in Sources */, 616A9CD22535D38200DB83CF /* UIKitHierarchyInspectorTests.swift in Sources */, ); diff --git a/Sources/DatadogObjc/Datadog+objc.swift b/Sources/DatadogObjc/Datadog+objc.swift index 6f9681c019..d8c7883460 100644 --- a/Sources/DatadogObjc/Datadog+objc.swift +++ b/Sources/DatadogObjc/Datadog+objc.swift @@ -110,4 +110,11 @@ public class DDDatadog: NSObject { public static func setTrackingConsent(consent: DDTrackingConsent) { Datadog.set(trackingConsent: consent.sdkConsent) } + +#if DD_SDK_COMPILED_FOR_TESTING + @objc + public static func flushAndDeinitialize() { + Datadog.flushAndDeinitialize() + } +#endif } diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m new file mode 100644 index 0000000000..5b0d63dd20 --- /dev/null +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m @@ -0,0 +1,111 @@ +/* +* 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. +*/ + +#import +@import DatadogObjc; +@import DatadogCrashReporting; + +// MARK: - DDUIKitRUMViewsPredicate + +@interface CustomDDUIKitRUMViewsPredicate: NSObject +@end + +@interface CustomDDUIKitRUMViewsPredicate () +@end + +@implementation CustomDDUIKitRUMViewsPredicate +- (DDRUMView * _Nullable)rumViewFor:(UIViewController * _Nonnull)viewController { return nil; } +@end + +// MARK: - DDUIKitRUMViewsPredicate + +@interface CustomDDUIKitRUMUserActionsPredicate: NSObject +@end + +@interface CustomDDUIKitRUMUserActionsPredicate () +@end + +@implementation CustomDDUIKitRUMUserActionsPredicate +- (DDRUMAction * _Nullable)rumActionWithTargetView:(UIView * _Nonnull)targetView { return nil; } +@end + +// MARK: - Tests + +@interface DDConfiguration_apiTests : XCTestCase +@end + +/* + * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. + */ +@implementation DDConfiguration_apiTests + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +- (void)testDDEndpointAPI { + [DDEndpoint eu]; + [DDEndpoint eu1]; + [DDEndpoint gov]; + [DDEndpoint us]; + [DDEndpoint us1]; + [DDEndpoint us1]; + [DDEndpoint us1_fed]; + [DDEndpoint us3]; +} + +- (void)testDDBatchSizeAPI { + DDBatchSizeSmall; DDBatchSizeMedium; DDBatchSizeLarge; +} + +- (void)testDDUploadFrequencyAPI { + DDUploadFrequencyRare; DDUploadFrequencyAverage; DDUploadFrequencyFrequent; +} + +- (void)testDDConfigurationAPI { + [DDConfiguration builderWithClientToken:@"" environment:@""]; + [DDConfiguration builderWithRumApplicationID:@"" clientToken:@"" environment:@""]; +} + +- (void)testDDConfigurationBuilderAPI { + DDConfigurationBuilder *builder = [DDConfiguration builderWithClientToken:@"" environment:@""]; + [builder enableLogging:YES]; + [builder enableTracing:YES]; + [builder enableRUM:YES]; + [builder enableCrashReportingUsing:[DDCrashReportingPlugin new]]; + [builder setWithEndpoint:[DDEndpoint us]]; + [builder setWithCustomLogsEndpoint:[NSURL new]]; + [builder setWithCustomTracesEndpoint:[NSURL new]]; + [builder setWithCustomRUMEndpoint:[NSURL new]]; + [builder trackURLSessionWithFirstPartyHosts:[NSSet setWithArray:@[]]]; + [builder setWithServiceName:@""]; + [builder setWithRumSessionsSamplingRate:50]; + [builder trackUIKitRUMViews]; + [builder trackUIKitRUMViewsUsing:[CustomDDUIKitRUMViewsPredicate new]]; + [builder trackUIKitRUMActions]; + [builder trackUIKitRUMActionsUsing:[CustomDDUIKitRUMUserActionsPredicate new]]; + [builder setRUMViewEventMapper:^DDRUMViewEvent * _Nonnull(DDRUMViewEvent * _Nonnull viewEvent) { + viewEvent.view.url = @""; + return viewEvent; + }]; + [builder setRUMResourceEventMapper:^DDRUMResourceEvent * _Nullable(DDRUMResourceEvent * _Nonnull resourceEvent) { + resourceEvent.resource.url = @""; + return resourceEvent; + }]; + [builder setRUMActionEventMapper:^DDRUMActionEvent * _Nullable(DDRUMActionEvent * _Nonnull actionEvent) { + return nil; + }]; + [builder setRUMErrorEventMapper:^DDRUMErrorEvent * _Nullable(DDRUMErrorEvent * _Nonnull errorEvent) { + return nil; + }]; + [builder setWithBatchSize:DDBatchSizeMedium]; + [builder setWithUploadFrequency:DDUploadFrequencyAverage]; + [builder setWithAdditionalConfiguration:@{}]; + [builder build]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDDatadog+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDDatadog+apiTests.m new file mode 100644 index 0000000000..008a55bb17 --- /dev/null +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDDatadog+apiTests.m @@ -0,0 +1,50 @@ +/* +* 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. +*/ + +#import +@import DatadogObjc; + +@interface DDDatadog_apiTests : XCTestCase +@end + +/* + * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. + */ +@implementation DDDatadog_apiTests + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +- (void)testDDTrackingConsentAPI { + [DDTrackingConsent granted]; + [DDTrackingConsent notGranted]; + [DDTrackingConsent pending]; +} + +- (void)testDDAppContextAPI { + [[DDAppContext alloc] initWithMainBundle:[NSBundle mainBundle]]; + [[DDAppContext alloc] init]; +} + +- (void)testDDDatadog { + DDConfiguration *configuration = [[DDConfiguration builderWithClientToken:@"abc" environment:@"def"] build]; + + [DDDatadog initializeWithAppContext:[[DDAppContext alloc] init] + trackingConsent:[DDTrackingConsent notGranted] + configuration:configuration]; + + DDSDKVerbosityLevel verbosity = [DDDatadog verbosityLevel]; + [DDDatadog setVerbosityLevel:verbosity]; + + [DDDatadog setUserInfoWithId:@"" name:@"" email:@"" extraInfo:@{}]; + [DDDatadog setTrackingConsentWithConsent:[DDTrackingConsent notGranted]]; + + [DDDatadog flushAndDeinitialize]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDGlobal+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDGlobal+apiTests.m new file mode 100644 index 0000000000..de515494f1 --- /dev/null +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDGlobal+apiTests.m @@ -0,0 +1,28 @@ +/* +* 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. +*/ + +#import +@import DatadogObjc; + +@interface DDGlobal_apiTests : XCTestCase +@end + +/* + * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. + */ +@implementation DDGlobal_apiTests + +- (void)testSharedTracerAPI { + id tracer = DDGlobal.sharedTracer; + DDGlobal.sharedTracer = tracer; +} + +- (void)testRUMAPI { + id rum = DDGlobal.rum; + DDGlobal.rum = rum; +} + +@end diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDNSURLSessionDelegate+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDNSURLSessionDelegate+apiTests.m new file mode 100644 index 0000000000..17ea657127 --- /dev/null +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDNSURLSessionDelegate+apiTests.m @@ -0,0 +1,28 @@ +/* +* 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. +*/ + +#import +@import DatadogObjc; + +@interface DDNSURLSessionDelegate_apiTests : XCTestCase +@end + +/* + * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. + */ +@implementation DDNSURLSessionDelegate_apiTests + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +- (void)testDDNSURLSessionDelegateAPI { + [[DDNSURLSessionDelegate alloc] init]; + [[DDNSURLSessionDelegate alloc] initWithAdditionalFirstPartyHosts:[NSSet setWithArray:@[]]]; +} + +#pragma clang diagnostic pop + +@end diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m new file mode 100644 index 0000000000..20e297d6c8 --- /dev/null +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m @@ -0,0 +1,76 @@ +/* +* 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. +*/ + +#import +@import DatadogObjc; + +@interface DDRUMMonitor_apiTests : XCTestCase +@end + +/* + * `DatadogObjc` APIs smoke tests - only check if the interface is available to Objc. + */ +@implementation DDRUMMonitor_apiTests + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" + +- (void)testDDRUMViewAPI { + DDRUMView *view = [[DDRUMView alloc] initWithName:@"abc" attributes:@{@"foo": @"bar"}]; + XCTAssertEqual(view.name, @"abc"); + XCTAssertNotNil(view.attributes[@"foo"]); // TODO: RUMM-1583 assert with `XCTAssertEqual` +} + +- (void)testDDRUMActionAPI { + DDRUMAction *action = [[DDRUMAction alloc] initWithName:@"abc" attributes:@{@"foo": @"bar"}]; + XCTAssertEqual(action.name, @"abc"); + XCTAssertNotNil(action.attributes[@"foo"]); // TODO: RUMM-1583 assert with `XCTAssertEqual` +} + +- (void)testDDRUMErrorSourceAPI { + DDRUMErrorSourceSource; DDRUMErrorSourceNetwork; DDRUMErrorSourceWebview; DDRUMErrorSourceConsole; DDRUMErrorSourceCustom; +} + +- (void)testDDRUMUserActionTypeAPI { + DDRUMUserActionTypeTap; DDRUMUserActionTypeScroll; DDRUMUserActionTypeSwipe; DDRUMUserActionTypeCustom; +} + +- (void)testDDRUMResourceTypeAPI { + DDRUMResourceTypeImage; DDRUMResourceTypeXhr; DDRUMResourceTypeBeacon; DDRUMResourceTypeCss; DDRUMResourceTypeDocument; + DDRUMResourceTypeFetch; DDRUMResourceTypeFont; DDRUMResourceTypeJs; DDRUMResourceTypeMedia; DDRUMResourceTypeOther; +} + +- (void)testDDRUMMethodAPI { + DDRUMMethodPost; DDRUMMethodGet; DDRUMMethodHead; DDRUMMethodPut; DDRUMMethodDelete; DDRUMMethodPatch; +} + +- (void)testDDRUMMonitorAPI { + UIViewController *anyVC = [UIViewController new]; + + DDRUMMonitor *monitor = [DDRUMMonitor new]; + [monitor startViewWithViewController:anyVC name:@"" attributes:@{}]; + [monitor stopViewWithViewController:anyVC attributes:@{}]; + [monitor startViewWithKey:@"" name:nil attributes:@{}]; + [monitor stopViewWithKey:@"" attributes:@{}]; + [monitor addErrorWithMessage:@"" source:DDRUMErrorSourceCustom stack:nil attributes:@{}]; + [monitor addErrorWithError:[NSError new] source:DDRUMErrorSourceNetwork attributes:@{}]; + [monitor startResourceLoadingWithResourceKey:@"" request:[NSURLRequest new] attributes:@{}]; + [monitor startResourceLoadingWithResourceKey:@"" url:[NSURL new] attributes:@{}]; + [monitor startResourceLoadingWithResourceKey:@"" httpMethod:DDRUMMethodGet urlString:@"" attributes:@{}]; + [monitor addResourceMetricsWithResourceKey:@"" metrics:[NSURLSessionTaskMetrics new] attributes:@{}]; + [monitor stopResourceLoadingWithResourceKey:@"" response:[NSURLResponse new] size:nil attributes:@{}]; + [monitor stopResourceLoadingWithResourceKey:@"" statusCode:nil kind:DDRUMResourceTypeOther size:nil attributes:@{}]; + [monitor stopResourceLoadingWithErrorWithResourceKey:@"" error:[NSError new] response:nil attributes:@{}]; + [monitor stopResourceLoadingWithErrorWithResourceKey:@"" errorMessage:@"" response:nil attributes:@{}]; + [monitor startUserActionWithType:DDRUMUserActionTypeSwipe name:@"" attributes:@{}]; + [monitor stopUserActionWithType:DDRUMUserActionTypeSwipe name:nil attributes:@{}]; + [monitor addUserActionWithType:DDRUMUserActionTypeTap name:@"" attributes:@{}]; + [monitor addAttributeForKey:@"" value:@""]; +} + +#pragma clang diagnostic pop + +@end From 905adebc36a48c93954f8ec283c34300d93ef402 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 31 Aug 2021 16:28:52 +0200 Subject: [PATCH 12/80] RUMM-1566 Improve CP tests by using targets w/ and w/o `use_frameworks!` --- bitrise.yml | 23 +- dependency-manager-tests/.gitignore | 6 +- .../CPProject.xcodeproj/project.pbxproj | 870 +++++++++++------- .../contents.xcworkspacedata | 2 +- .../CPProjectNoUseFrameworks.xcscheme | 98 ++ ...scheme => CPProjectUseFrameworks.xcscheme} | 30 +- .../CPProject/Base.lproj/Main.storyboard | 54 +- .../cocoapods/CPProject/Info.plist | 2 + .../CPProjectTests/CPProjectTests.swift | 6 +- dependency-manager-tests/cocoapods/Makefile | 2 +- .../cocoapods/Podfile.src | 15 +- 11 files changed, 721 insertions(+), 387 deletions(-) create mode 100644 dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectNoUseFrameworks.xcscheme rename dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/{CPProject.xcscheme => CPProjectUseFrameworks.xcscheme} (76%) diff --git a/bitrise.yml b/bitrise.yml index da95cb8bb9..7fd3eabd0d 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -214,7 +214,7 @@ workflows: set -e make test-spm ci=${CI} - xcodebuild: - title: Build SPMProject for tests - Catalyst + title: Check Mac Catalyst compatibility (build SPMProject for Catalyst) inputs: - scheme: SPMProject - destination: platform=macOS,variant=Mac Catalyst @@ -230,7 +230,6 @@ workflows: - project_path: "$BITRISE_SOURCE_DIR/dependency-manager-tests/spm/SPMProject.xcodeproj" - xcpretty_test_options: --color --report html --output "${BITRISE_DEPLOY_DIR}/SPMProject-tests.html" - script: - # Carthage doesn't support Catalyst, so we don't test CTProject for `variant=Mac Catalyst` title: Test Carthage compatibility inputs: - content: |- @@ -253,22 +252,24 @@ workflows: #!/usr/bin/env bash set -e make test-cocoapods ci=${CI} - - xcodebuild: - title: Build CPProject for tests - Catalyst + - xcode-test: + title: Run CPProject tests with 'use_frameworks!' inputs: - - scheme: CPProject - - destination: platform=macOS,variant=Mac Catalyst - - project_path: "$BITRISE_SOURCE_DIR/dependency-manager-tests/cocoapods/CPProject.xcodeproj" - - xcpretty_test_options: --color --report html --output "${BITRISE_DEPLOY_DIR}/CPProject-catalyst-sanity-check.html" + - scheme: CPProjectUseFrameworks + - simulator_device: iPhone 11 + - is_clean_build: 'yes' + - cache_level: none + - project_path: "$BITRISE_SOURCE_DIR/dependency-manager-tests/cocoapods/CPProject.xcworkspace" + - xcpretty_test_options: --color --report html --output "${BITRISE_DEPLOY_DIR}/CPProject-use_frameworks-tests.html" - xcode-test: - title: Run CPProject tests + title: Run CPProject tests with no 'use_frameworks!' inputs: - - scheme: CPProject + - scheme: CPProjectNoUseFrameworks - simulator_device: iPhone 11 - is_clean_build: 'yes' - cache_level: none - project_path: "$BITRISE_SOURCE_DIR/dependency-manager-tests/cocoapods/CPProject.xcworkspace" - - xcpretty_test_options: --color --report html --output "${BITRISE_DEPLOY_DIR}/CPProject-tests.html" + - xcpretty_test_options: --color --report html --output "${BITRISE_DEPLOY_DIR}/CPProject-no_use_frameworks-tests.html" create_dogfooding_pr: description: |- diff --git a/dependency-manager-tests/.gitignore b/dependency-manager-tests/.gitignore index ffc5a47759..d068bfb2cf 100644 --- a/dependency-manager-tests/.gitignore +++ b/dependency-manager-tests/.gitignore @@ -15,6 +15,6 @@ xcuserdata # Cocoapods test # - ignore `Podfile.lock` and `Podfile` as they will be re-created for every test run -/cocoapods/Pods -/cocoapods/Podfile.lock -/cocoapods/Podfile +/cocoapods/**/Pods +/cocoapods/**/Podfile.lock +/cocoapods/**/Podfile diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj index d1ec7f0529..4d907dd298 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj @@ -7,181 +7,224 @@ objects = { /* Begin PBXBuildFile section */ - 531C3FE9C2034D6CA1287A2B /* Pods_CPProject_CPProjectTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93E0EA15A59822C848CE2682 /* Pods_CPProject_CPProjectTests.framework */; }; - 61C363932436318E00C4D4E6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C363922436318E00C4D4E6 /* AppDelegate.swift */; }; - 61C363952436318E00C4D4E6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C363942436318E00C4D4E6 /* SceneDelegate.swift */; }; - 61C363972436318E00C4D4E6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C363962436318E00C4D4E6 /* ViewController.swift */; }; - 61C3639A2436318E00C4D4E6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61C363982436318E00C4D4E6 /* Main.storyboard */; }; - 61C3639F2436319000C4D4E6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61C3639D2436319000C4D4E6 /* LaunchScreen.storyboard */; }; - 61C363AA2436319000C4D4E6 /* CPProjectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C363A92436319000C4D4E6 /* CPProjectUITests.swift */; }; - 61C364552437568300C4D4E6 /* CPProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C364542437568300C4D4E6 /* CPProjectTests.swift */; }; - 7EFE9B5530413599C43DEF11 /* Pods_CPProject.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 394ABB8D53A46EFC06232B89 /* Pods_CPProject.framework */; }; - 8051FF40D3D1CDD4E547A094 /* Pods_CPProject_CPProjectUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16AA6425CE83208EBE1AEF5B /* Pods_CPProject_CPProjectUITests.framework */; }; + 2FF2A19F9C78FC98880874FF /* Pods_Common_CPProjectUseFrameworks.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F6BCFD46A729DA7F32FC9 /* Pods_Common_CPProjectUseFrameworks.framework */; }; + 61373B5926E0E7C900E0F46E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30626E0E278006EDF53 /* AppDelegate.swift */; }; + 61373B5A26E0E7CD00E0F46E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30826E0E278006EDF53 /* SceneDelegate.swift */; }; + 61373B5B26E0E7D100E0F46E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30A26E0E278006EDF53 /* ViewController.swift */; }; + 61373B5C26E0E7D800E0F46E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61B8C30C26E0E278006EDF53 /* Main.storyboard */; }; + 61373B5E26E0E7DF00E0F46E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61B8C31126E0E27A006EDF53 /* LaunchScreen.storyboard */; }; + 61373B5F26E0E7E400E0F46E /* CPProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C31D26E0E27A006EDF53 /* CPProjectTests.swift */; }; + 61373B6126E0EA2500E0F46E /* CPProjectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C32826E0E27A006EDF53 /* CPProjectUITests.swift */; }; + 61B8C30726E0E278006EDF53 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30626E0E278006EDF53 /* AppDelegate.swift */; }; + 61B8C30926E0E278006EDF53 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30826E0E278006EDF53 /* SceneDelegate.swift */; }; + 61B8C30B26E0E278006EDF53 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C30A26E0E278006EDF53 /* ViewController.swift */; }; + 61B8C30E26E0E278006EDF53 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61B8C30C26E0E278006EDF53 /* Main.storyboard */; }; + 61B8C31326E0E27A006EDF53 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61B8C31126E0E27A006EDF53 /* LaunchScreen.storyboard */; }; + 61B8C31E26E0E27A006EDF53 /* CPProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C31D26E0E27A006EDF53 /* CPProjectTests.swift */; }; + 61B8C32926E0E27A006EDF53 /* CPProjectUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61B8C32826E0E27A006EDF53 /* CPProjectUITests.swift */; }; + A154B5926600D17ECDD21D84 /* libPods-CPProjectNoUseFrameworksTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B2DA1BBBF19485F5F93D1E62 /* libPods-CPProjectNoUseFrameworksTests.a */; }; + FB53D921A5A47E18B605B20B /* libPods-Common-CPProjectNoUseFrameworks.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 99AEBB22D577EFFA7CED4C35 /* libPods-Common-CPProjectNoUseFrameworks.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 61C363A62436319000C4D4E6 /* PBXContainerItemProxy */ = { + 61373B3F26E0E78500E0F46E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 61C363872436318E00C4D4E6 /* Project object */; + containerPortal = 61B8C2FB26E0E278006EDF53 /* Project object */; proxyType = 1; - remoteGlobalIDString = 61C3638E2436318E00C4D4E6; + remoteGlobalIDString = 61373B2826E0E78300E0F46E; + remoteInfo = CPProjectNoUseFrameworks; + }; + 61373B4A26E0E78500E0F46E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 61B8C2FB26E0E278006EDF53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 61373B2826E0E78300E0F46E; + remoteInfo = CPProjectNoUseFrameworks; + }; + 61B8C31A26E0E27A006EDF53 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 61B8C2FB26E0E278006EDF53 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 61B8C30226E0E278006EDF53; remoteInfo = CPProject; }; - 61C364572437568300C4D4E6 /* PBXContainerItemProxy */ = { + 61B8C32526E0E27A006EDF53 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 61C363872436318E00C4D4E6 /* Project object */; + containerPortal = 61B8C2FB26E0E278006EDF53 /* Project object */; proxyType = 1; - remoteGlobalIDString = 61C3638E2436318E00C4D4E6; + remoteGlobalIDString = 61B8C30226E0E278006EDF53; remoteInfo = CPProject; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 023E9F71B1FFBEAF47AD599B /* Pods-CPProject-CPProjectUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject-CPProjectUITests.debug.xcconfig"; path = "Target Support Files/Pods-CPProject-CPProjectUITests/Pods-CPProject-CPProjectUITests.debug.xcconfig"; sourceTree = ""; }; - 0D9B145559078D7130D6AA72 /* Pods-CPProject-CPProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject-CPProjectTests.release.xcconfig"; path = "Target Support Files/Pods-CPProject-CPProjectTests/Pods-CPProject-CPProjectTests.release.xcconfig"; sourceTree = ""; }; - 16AA6425CE83208EBE1AEF5B /* Pods_CPProject_CPProjectUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CPProject_CPProjectUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 394ABB8D53A46EFC06232B89 /* Pods_CPProject.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CPProject.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5ACCCE7D06C5874130D7D6D4 /* Pods-CPProjectUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectUITests.debug.xcconfig"; path = "Target Support Files/Pods-CPProjectUITests/Pods-CPProjectUITests.debug.xcconfig"; sourceTree = ""; }; - 5AD49F84C2EA2EE5CB99586F /* Pods-CPProject.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject.debug.xcconfig"; path = "Target Support Files/Pods-CPProject/Pods-CPProject.debug.xcconfig"; sourceTree = ""; }; - 61C3638F2436318E00C4D4E6 /* CPProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CPProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 61C363922436318E00C4D4E6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 61C363942436318E00C4D4E6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 61C363962436318E00C4D4E6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 61C363992436318E00C4D4E6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 61C3639E2436319000C4D4E6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 61C363A02436319000C4D4E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 61C363A52436319000C4D4E6 /* CPProjectUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 61C363A92436319000C4D4E6 /* CPProjectUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPProjectUITests.swift; sourceTree = ""; }; - 61C363AB2436319000C4D4E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 61C364522437568300C4D4E6 /* CPProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 61C364542437568300C4D4E6 /* CPProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPProjectTests.swift; sourceTree = ""; }; - 61C364562437568300C4D4E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 61ED26E42461D29E00752D66 /* Datadog.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Datadog.xcconfig; sourceTree = ""; }; - 61ED26E52461D29E00752D66 /* Datadog.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Datadog.local.xcconfig; sourceTree = ""; }; - 6C29934101A436B996950B9C /* Pods-CPProject-CPProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject-CPProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-CPProject-CPProjectTests/Pods-CPProject-CPProjectTests.debug.xcconfig"; sourceTree = ""; }; - 93E0EA15A59822C848CE2682 /* Pods_CPProject_CPProjectTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CPProject_CPProjectTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9E33ADFE7E2CECC33A07A795 /* Pods-CPProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-CPProjectTests/Pods-CPProjectTests.debug.xcconfig"; sourceTree = ""; }; - A9A539012CEB94D3DDBEEFAF /* Pods-CPProjectUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectUITests.release.xcconfig"; path = "Target Support Files/Pods-CPProjectUITests/Pods-CPProjectUITests.release.xcconfig"; sourceTree = ""; }; - C7DE97263B77DC87F0FC27F0 /* Pods-CPProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject.release.xcconfig"; path = "Target Support Files/Pods-CPProject/Pods-CPProject.release.xcconfig"; sourceTree = ""; }; - E4221836EB9A6AB7B55FCA4E /* Pods-CPProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectTests.release.xcconfig"; path = "Target Support Files/Pods-CPProjectTests/Pods-CPProjectTests.release.xcconfig"; sourceTree = ""; }; - EB80E12EA30834731C20BC88 /* Pods-CPProject-CPProjectUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProject-CPProjectUITests.release.xcconfig"; path = "Target Support Files/Pods-CPProject-CPProjectUITests/Pods-CPProject-CPProjectUITests.release.xcconfig"; sourceTree = ""; }; + 01FD54C4011C8F113E285585 /* Pods-Common-CPProjectNoUseFrameworks.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks/Pods-Common-CPProjectNoUseFrameworks.debug.xcconfig"; sourceTree = ""; }; + 02265FB44AE08BD3D3454745 /* Pods-CPProjectNoUseFrameworksTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectNoUseFrameworksTests.release.xcconfig"; path = "Target Support Files/Pods-CPProjectNoUseFrameworksTests/Pods-CPProjectNoUseFrameworksTests.release.xcconfig"; sourceTree = ""; }; + 0614566C30EF2E2D7BAF1117 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.debug.xcconfig"; sourceTree = ""; }; + 17E75BD7BB8D512DB90A9632 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.release.xcconfig"; sourceTree = ""; }; + 1F5643EF45F5157D39302AB4 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.debug.xcconfig"; sourceTree = ""; }; + 3D97712BEE25C98C566E565A /* Pods-Common-CPProjectNoUseFrameworks.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks/Pods-Common-CPProjectNoUseFrameworks.release.xcconfig"; sourceTree = ""; }; + 424F6BCFD46A729DA7F32FC9 /* Pods_Common_CPProjectUseFrameworks.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Common_CPProjectUseFrameworks.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 51776B4903CB23F278458BB6 /* Pods-CPProjectNoUseFrameworksTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPProjectNoUseFrameworksTests.debug.xcconfig"; path = "Target Support Files/Pods-CPProjectNoUseFrameworksTests/Pods-CPProjectNoUseFrameworksTests.debug.xcconfig"; sourceTree = ""; }; + 586B9607FFDD69DAD0C23F22 /* Pods-Common-CPProjectUseFrameworks.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks/Pods-Common-CPProjectUseFrameworks.debug.xcconfig"; sourceTree = ""; }; + 61373B2926E0E78300E0F46E /* CPProjectNoUseFrameworks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CPProjectNoUseFrameworks.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 61373B3E26E0E78500E0F46E /* CPProjectNoUseFrameworksTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectNoUseFrameworksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 61373B4926E0E78500E0F46E /* CPProjectNoUseFrameworksUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectNoUseFrameworksUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 61B8C30326E0E278006EDF53 /* CPProjectUseFrameworks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CPProjectUseFrameworks.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 61B8C30626E0E278006EDF53 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 61B8C30826E0E278006EDF53 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 61B8C30A26E0E278006EDF53 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 61B8C30D26E0E278006EDF53 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 61B8C31226E0E27A006EDF53 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 61B8C31426E0E27A006EDF53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 61B8C31926E0E27A006EDF53 /* CPProjectUseFrameworksTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectUseFrameworksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 61B8C31D26E0E27A006EDF53 /* CPProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPProjectTests.swift; sourceTree = ""; }; + 61B8C31F26E0E27A006EDF53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 61B8C32426E0E27A006EDF53 /* CPProjectUseFrameworksUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CPProjectUseFrameworksUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 61B8C32826E0E27A006EDF53 /* CPProjectUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPProjectUITests.swift; sourceTree = ""; }; + 61B8C32A26E0E27A006EDF53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7BC8294072C54F7BAD098B99 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.debug.xcconfig"; sourceTree = ""; }; + 811F6A3208073739F8E4C83E /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.release.xcconfig"; sourceTree = ""; }; + 93617EDCE36F9B3020CD3EB1 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.release.xcconfig"; sourceTree = ""; }; + 99AEBB22D577EFFA7CED4C35 /* libPods-Common-CPProjectNoUseFrameworks.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Common-CPProjectNoUseFrameworks.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9D6D3B3BC8427FCEF169BBEC /* Pods-Common-CPProjectUseFrameworks.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks/Pods-Common-CPProjectUseFrameworks.release.xcconfig"; sourceTree = ""; }; + B2DA1BBBF19485F5F93D1E62 /* libPods-CPProjectNoUseFrameworksTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CPProjectNoUseFrameworksTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + BAC50E424C491462E9E6E288 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.debug.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests/Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.debug.xcconfig"; sourceTree = ""; }; + BC677EB73CB7FE4A474BB0D5 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.release.xcconfig"; path = "Target Support Files/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests/Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 61C3638C2436318E00C4D4E6 /* Frameworks */ = { + 61373B2626E0E78300E0F46E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7EFE9B5530413599C43DEF11 /* Pods_CPProject.framework in Frameworks */, + FB53D921A5A47E18B605B20B /* libPods-Common-CPProjectNoUseFrameworks.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 61C363A22436319000C4D4E6 /* Frameworks */ = { + 61373B3B26E0E78500E0F46E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8051FF40D3D1CDD4E547A094 /* Pods_CPProject_CPProjectUITests.framework in Frameworks */, + A154B5926600D17ECDD21D84 /* libPods-CPProjectNoUseFrameworksTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 61C3644F2437568300C4D4E6 /* Frameworks */ = { + 61373B4626E0E78500E0F46E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C30026E0E278006EDF53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2FF2A19F9C78FC98880874FF /* Pods_Common_CPProjectUseFrameworks.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C31626E0E27A006EDF53 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C32126E0E27A006EDF53 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 531C3FE9C2034D6CA1287A2B /* Pods_CPProject_CPProjectTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0157255130C038561709B7CF /* Pods */ = { - isa = PBXGroup; - children = ( - 5AD49F84C2EA2EE5CB99586F /* Pods-CPProject.debug.xcconfig */, - C7DE97263B77DC87F0FC27F0 /* Pods-CPProject.release.xcconfig */, - 9E33ADFE7E2CECC33A07A795 /* Pods-CPProjectTests.debug.xcconfig */, - E4221836EB9A6AB7B55FCA4E /* Pods-CPProjectTests.release.xcconfig */, - 5ACCCE7D06C5874130D7D6D4 /* Pods-CPProjectUITests.debug.xcconfig */, - A9A539012CEB94D3DDBEEFAF /* Pods-CPProjectUITests.release.xcconfig */, - 6C29934101A436B996950B9C /* Pods-CPProject-CPProjectTests.debug.xcconfig */, - 0D9B145559078D7130D6AA72 /* Pods-CPProject-CPProjectTests.release.xcconfig */, - 023E9F71B1FFBEAF47AD599B /* Pods-CPProject-CPProjectUITests.debug.xcconfig */, - EB80E12EA30834731C20BC88 /* Pods-CPProject-CPProjectUITests.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 61C363862436318E00C4D4E6 = { + 61B8C2FA26E0E278006EDF53 = { isa = PBXGroup; children = ( - 61ED26E32461D29E00752D66 /* xcconfigs */, - 61C363912436318E00C4D4E6 /* CPProject */, - 61C364532437568300C4D4E6 /* CPProjectTests */, - 61C363A82436319000C4D4E6 /* CPProjectUITests */, - 61C363902436318E00C4D4E6 /* Products */, - 0157255130C038561709B7CF /* Pods */, - A0508E6A3FA3F45E84391D24 /* Frameworks */, + 61B8C30526E0E278006EDF53 /* CPProject */, + 61B8C31C26E0E27A006EDF53 /* CPProjectTests */, + 61B8C32726E0E27A006EDF53 /* CPProjectUITests */, + 61B8C30426E0E278006EDF53 /* Products */, + 8F60E2E92A8515F0E47A9B30 /* Pods */, + 914DF2FA9D66773F6CA5E53E /* Frameworks */, ); sourceTree = ""; }; - 61C363902436318E00C4D4E6 /* Products */ = { + 61B8C30426E0E278006EDF53 /* Products */ = { isa = PBXGroup; children = ( - 61C3638F2436318E00C4D4E6 /* CPProject.app */, - 61C363A52436319000C4D4E6 /* CPProjectUITests.xctest */, - 61C364522437568300C4D4E6 /* CPProjectTests.xctest */, + 61B8C30326E0E278006EDF53 /* CPProjectUseFrameworks.app */, + 61B8C31926E0E27A006EDF53 /* CPProjectUseFrameworksTests.xctest */, + 61B8C32426E0E27A006EDF53 /* CPProjectUseFrameworksUITests.xctest */, + 61373B2926E0E78300E0F46E /* CPProjectNoUseFrameworks.app */, + 61373B3E26E0E78500E0F46E /* CPProjectNoUseFrameworksTests.xctest */, + 61373B4926E0E78500E0F46E /* CPProjectNoUseFrameworksUITests.xctest */, ); name = Products; sourceTree = ""; }; - 61C363912436318E00C4D4E6 /* CPProject */ = { + 61B8C30526E0E278006EDF53 /* CPProject */ = { isa = PBXGroup; children = ( - 61C363922436318E00C4D4E6 /* AppDelegate.swift */, - 61C363942436318E00C4D4E6 /* SceneDelegate.swift */, - 61C363962436318E00C4D4E6 /* ViewController.swift */, - 61C363982436318E00C4D4E6 /* Main.storyboard */, - 61C3639D2436319000C4D4E6 /* LaunchScreen.storyboard */, - 61C363A02436319000C4D4E6 /* Info.plist */, + 61B8C30626E0E278006EDF53 /* AppDelegate.swift */, + 61B8C30826E0E278006EDF53 /* SceneDelegate.swift */, + 61B8C30A26E0E278006EDF53 /* ViewController.swift */, + 61B8C30C26E0E278006EDF53 /* Main.storyboard */, + 61B8C31126E0E27A006EDF53 /* LaunchScreen.storyboard */, + 61B8C31426E0E27A006EDF53 /* Info.plist */, ); path = CPProject; sourceTree = ""; }; - 61C363A82436319000C4D4E6 /* CPProjectUITests */ = { + 61B8C31C26E0E27A006EDF53 /* CPProjectTests */ = { isa = PBXGroup; children = ( - 61C363A92436319000C4D4E6 /* CPProjectUITests.swift */, - 61C363AB2436319000C4D4E6 /* Info.plist */, + 61B8C31D26E0E27A006EDF53 /* CPProjectTests.swift */, + 61B8C31F26E0E27A006EDF53 /* Info.plist */, ); - path = CPProjectUITests; + path = CPProjectTests; sourceTree = ""; }; - 61C364532437568300C4D4E6 /* CPProjectTests */ = { + 61B8C32726E0E27A006EDF53 /* CPProjectUITests */ = { isa = PBXGroup; children = ( - 61C364542437568300C4D4E6 /* CPProjectTests.swift */, - 61C364562437568300C4D4E6 /* Info.plist */, + 61B8C32826E0E27A006EDF53 /* CPProjectUITests.swift */, + 61B8C32A26E0E27A006EDF53 /* Info.plist */, ); - path = CPProjectTests; + path = CPProjectUITests; sourceTree = ""; }; - 61ED26E32461D29E00752D66 /* xcconfigs */ = { + 8F60E2E92A8515F0E47A9B30 /* Pods */ = { isa = PBXGroup; children = ( - 61ED26E42461D29E00752D66 /* Datadog.xcconfig */, - 61ED26E52461D29E00752D66 /* Datadog.local.xcconfig */, + 586B9607FFDD69DAD0C23F22 /* Pods-Common-CPProjectUseFrameworks.debug.xcconfig */, + 9D6D3B3BC8427FCEF169BBEC /* Pods-Common-CPProjectUseFrameworks.release.xcconfig */, + 0614566C30EF2E2D7BAF1117 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.debug.xcconfig */, + 811F6A3208073739F8E4C83E /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksTests.release.xcconfig */, + BAC50E424C491462E9E6E288 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.debug.xcconfig */, + 93617EDCE36F9B3020CD3EB1 /* Pods-Common-CPProjectUseFrameworks-CPProjectUseFrameworksUITests.release.xcconfig */, + 01FD54C4011C8F113E285585 /* Pods-Common-CPProjectNoUseFrameworks.debug.xcconfig */, + 3D97712BEE25C98C566E565A /* Pods-Common-CPProjectNoUseFrameworks.release.xcconfig */, + 1F5643EF45F5157D39302AB4 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.debug.xcconfig */, + 17E75BD7BB8D512DB90A9632 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksTests.release.xcconfig */, + 7BC8294072C54F7BAD098B99 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.debug.xcconfig */, + BC677EB73CB7FE4A474BB0D5 /* Pods-Common-CPProjectNoUseFrameworks-CPProjectNoUseFrameworksUITests.release.xcconfig */, + 51776B4903CB23F278458BB6 /* Pods-CPProjectNoUseFrameworksTests.debug.xcconfig */, + 02265FB44AE08BD3D3454745 /* Pods-CPProjectNoUseFrameworksTests.release.xcconfig */, ); - name = xcconfigs; - path = ../../xcconfigs; + path = Pods; sourceTree = ""; }; - A0508E6A3FA3F45E84391D24 /* Frameworks */ = { + 914DF2FA9D66773F6CA5E53E /* Frameworks */ = { isa = PBXGroup; children = ( - 394ABB8D53A46EFC06232B89 /* Pods_CPProject.framework */, - 93E0EA15A59822C848CE2682 /* Pods_CPProject_CPProjectTests.framework */, - 16AA6425CE83208EBE1AEF5B /* Pods_CPProject_CPProjectUITests.framework */, + 424F6BCFD46A729DA7F32FC9 /* Pods_Common_CPProjectUseFrameworks.framework */, + 99AEBB22D577EFFA7CED4C35 /* libPods-Common-CPProjectNoUseFrameworks.a */, + B2DA1BBBF19485F5F93D1E62 /* libPods-CPProjectNoUseFrameworksTests.a */, ); name = Frameworks; sourceTree = ""; @@ -189,90 +232,150 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 61C3638E2436318E00C4D4E6 /* CPProject */ = { + 61373B2826E0E78300E0F46E /* CPProjectNoUseFrameworks */ = { isa = PBXNativeTarget; - buildConfigurationList = 61C363AE2436319000C4D4E6 /* Build configuration list for PBXNativeTarget "CPProject" */; + buildConfigurationList = 61373B5026E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworks" */; buildPhases = ( - B5E3E8D262C8003866F38F23 /* [CP] Check Pods Manifest.lock */, - 61C3638B2436318E00C4D4E6 /* Sources */, - 61C3638C2436318E00C4D4E6 /* Frameworks */, - 61C3638D2436318E00C4D4E6 /* Resources */, - 61C3645E24376AEF00C4D4E6 /* ⚙️ Run linter */, - 1D03F40EC3476C946BC0FE93 /* [CP] Embed Pods Frameworks */, + 8D0504D0CD7CAFBBB5B63F13 /* [CP] Check Pods Manifest.lock */, + 61373B2526E0E78300E0F46E /* Sources */, + 61373B2626E0E78300E0F46E /* Frameworks */, + 61373B2726E0E78300E0F46E /* Resources */, ); buildRules = ( ); dependencies = ( ); - name = CPProject; - productName = CPProject; - productReference = 61C3638F2436318E00C4D4E6 /* CPProject.app */; + name = CPProjectNoUseFrameworks; + productName = CPProjectNoUseFrameworks; + productReference = 61373B2926E0E78300E0F46E /* CPProjectNoUseFrameworks.app */; productType = "com.apple.product-type.application"; }; - 61C363A42436319000C4D4E6 /* CPProjectUITests */ = { + 61373B3D26E0E78500E0F46E /* CPProjectNoUseFrameworksTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 61C363B12436319000C4D4E6 /* Build configuration list for PBXNativeTarget "CPProjectUITests" */; + buildConfigurationList = 61373B5326E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworksTests" */; buildPhases = ( - 46BAFB96F3CF8A0594FB8A8D /* [CP] Check Pods Manifest.lock */, - 61C363A12436319000C4D4E6 /* Sources */, - 61C363A22436319000C4D4E6 /* Frameworks */, - 61C363A32436319000C4D4E6 /* Resources */, - 567E049B7EEB3FF26416E3A0 /* [CP] Embed Pods Frameworks */, + D6011F89065F1B0B4B52E465 /* [CP] Check Pods Manifest.lock */, + 61373B3A26E0E78500E0F46E /* Sources */, + 61373B3B26E0E78500E0F46E /* Frameworks */, + 61373B3C26E0E78500E0F46E /* Resources */, ); buildRules = ( ); dependencies = ( - 61C363A72436319000C4D4E6 /* PBXTargetDependency */, + 61373B4026E0E78500E0F46E /* PBXTargetDependency */, ); - name = CPProjectUITests; - productName = CPProjectUITests; - productReference = 61C363A52436319000C4D4E6 /* CPProjectUITests.xctest */; + name = CPProjectNoUseFrameworksTests; + productName = CPProjectNoUseFrameworksTests; + productReference = 61373B3E26E0E78500E0F46E /* CPProjectNoUseFrameworksTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 61373B4826E0E78500E0F46E /* CPProjectNoUseFrameworksUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 61373B5626E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworksUITests" */; + buildPhases = ( + 61373B4526E0E78500E0F46E /* Sources */, + 61373B4626E0E78500E0F46E /* Frameworks */, + 61373B4726E0E78500E0F46E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 61373B4B26E0E78500E0F46E /* PBXTargetDependency */, + ); + name = CPProjectNoUseFrameworksUITests; + productName = CPProjectNoUseFrameworksUITests; + productReference = 61373B4926E0E78500E0F46E /* CPProjectNoUseFrameworksUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; - 61C364512437568300C4D4E6 /* CPProjectTests */ = { + 61B8C30226E0E278006EDF53 /* CPProjectUseFrameworks */ = { + isa = PBXNativeTarget; + buildConfigurationList = 61B8C32D26E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworks" */; + buildPhases = ( + 5645FD027BB938478818FE88 /* [CP] Check Pods Manifest.lock */, + 61B8C2FF26E0E278006EDF53 /* Sources */, + 61B8C30026E0E278006EDF53 /* Frameworks */, + 61B8C30126E0E278006EDF53 /* Resources */, + 31384BB2056A865F3ED23F38 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CPProjectUseFrameworks; + productName = CPProject; + productReference = 61B8C30326E0E278006EDF53 /* CPProjectUseFrameworks.app */; + productType = "com.apple.product-type.application"; + }; + 61B8C31826E0E27A006EDF53 /* CPProjectUseFrameworksTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 61C3645B2437568300C4D4E6 /* Build configuration list for PBXNativeTarget "CPProjectTests" */; + buildConfigurationList = 61B8C33026E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworksTests" */; buildPhases = ( - E0C0BFBDF1EBCFBACB1B053D /* [CP] Check Pods Manifest.lock */, - 61C3644E2437568300C4D4E6 /* Sources */, - 61C3644F2437568300C4D4E6 /* Frameworks */, - 61C364502437568300C4D4E6 /* Resources */, - D8E080EFA5BC75427C8EA6A2 /* [CP] Embed Pods Frameworks */, + 61B8C31526E0E27A006EDF53 /* Sources */, + 61B8C31626E0E27A006EDF53 /* Frameworks */, + 61B8C31726E0E27A006EDF53 /* Resources */, ); buildRules = ( ); dependencies = ( - 61C364582437568300C4D4E6 /* PBXTargetDependency */, + 61B8C31B26E0E27A006EDF53 /* PBXTargetDependency */, ); - name = CPProjectTests; + name = CPProjectUseFrameworksTests; productName = CPProjectTests; - productReference = 61C364522437568300C4D4E6 /* CPProjectTests.xctest */; + productReference = 61B8C31926E0E27A006EDF53 /* CPProjectUseFrameworksTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 61B8C32326E0E27A006EDF53 /* CPProjectUseFrameworksUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 61B8C33326E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworksUITests" */; + buildPhases = ( + 61B8C32026E0E27A006EDF53 /* Sources */, + 61B8C32126E0E27A006EDF53 /* Frameworks */, + 61B8C32226E0E27A006EDF53 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 61B8C32626E0E27A006EDF53 /* PBXTargetDependency */, + ); + name = CPProjectUseFrameworksUITests; + productName = CPProjectUITests; + productReference = 61B8C32426E0E27A006EDF53 /* CPProjectUseFrameworksUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 61C363872436318E00C4D4E6 /* Project object */ = { + 61B8C2FB26E0E278006EDF53 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1140; + LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; - ORGANIZATIONNAME = Datadog; TargetAttributes = { - 61C3638E2436318E00C4D4E6 = { - CreatedOnToolsVersion = 11.4; + 61373B2826E0E78300E0F46E = { + CreatedOnToolsVersion = 12.5; + }; + 61373B3D26E0E78500E0F46E = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 61373B2826E0E78300E0F46E; + }; + 61373B4826E0E78500E0F46E = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 61373B2826E0E78300E0F46E; }; - 61C363A42436319000C4D4E6 = { - CreatedOnToolsVersion = 11.4; - TestTargetID = 61C3638E2436318E00C4D4E6; + 61B8C30226E0E278006EDF53 = { + CreatedOnToolsVersion = 12.5; }; - 61C364512437568300C4D4E6 = { - CreatedOnToolsVersion = 11.4; - TestTargetID = 61C3638E2436318E00C4D4E6; + 61B8C31826E0E27A006EDF53 = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 61B8C30226E0E278006EDF53; + }; + 61B8C32326E0E27A006EDF53 = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 61B8C30226E0E278006EDF53; }; }; }; - buildConfigurationList = 61C3638A2436318E00C4D4E6 /* Build configuration list for PBXProject "CPProject" */; + buildConfigurationList = 61B8C2FE26E0E278006EDF53 /* Build configuration list for PBXProject "CPProject" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -280,102 +383,89 @@ en, Base, ); - mainGroup = 61C363862436318E00C4D4E6; - productRefGroup = 61C363902436318E00C4D4E6 /* Products */; + mainGroup = 61B8C2FA26E0E278006EDF53; + productRefGroup = 61B8C30426E0E278006EDF53 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 61C3638E2436318E00C4D4E6 /* CPProject */, - 61C364512437568300C4D4E6 /* CPProjectTests */, - 61C363A42436319000C4D4E6 /* CPProjectUITests */, + 61B8C30226E0E278006EDF53 /* CPProjectUseFrameworks */, + 61B8C31826E0E27A006EDF53 /* CPProjectUseFrameworksTests */, + 61B8C32326E0E27A006EDF53 /* CPProjectUseFrameworksUITests */, + 61373B2826E0E78300E0F46E /* CPProjectNoUseFrameworks */, + 61373B3D26E0E78500E0F46E /* CPProjectNoUseFrameworksTests */, + 61373B4826E0E78500E0F46E /* CPProjectNoUseFrameworksUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 61C3638D2436318E00C4D4E6 /* Resources */ = { + 61373B2726E0E78300E0F46E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 61C3639F2436319000C4D4E6 /* LaunchScreen.storyboard in Resources */, - 61C3639A2436318E00C4D4E6 /* Main.storyboard in Resources */, + 61373B5E26E0E7DF00E0F46E /* LaunchScreen.storyboard in Resources */, + 61373B5C26E0E7D800E0F46E /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 61C363A32436319000C4D4E6 /* Resources */ = { + 61373B3C26E0E78500E0F46E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 61C364502437568300C4D4E6 /* Resources */ = { + 61373B4726E0E78500E0F46E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 1D03F40EC3476C946BC0FE93 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; + 61B8C30126E0E278006EDF53 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject/Pods-CPProject-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject/Pods-CPProject-frameworks-${CONFIGURATION}-output-files.xcfilelist", + 61B8C31326E0E27A006EDF53 /* LaunchScreen.storyboard in Resources */, + 61B8C30E26E0E278006EDF53 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CPProject/Pods-CPProject-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; - 46BAFB96F3CF8A0594FB8A8D /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; + 61B8C31726E0E27A006EDF53 /* Resources */ = { + isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CPProject-CPProjectUITests-checkManifestLockResult.txt", + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C32226E0E27A006EDF53 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; }; - 567E049B7EEB3FF26416E3A0 /* [CP] Embed Pods Frameworks */ = { +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 31384BB2056A865F3ED23F38 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectUITests/Pods-CPProject-CPProjectUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Common-CPProjectUseFrameworks/Pods-Common-CPProjectUseFrameworks-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectUITests/Pods-CPProject-CPProjectUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Common-CPProjectUseFrameworks/Pods-Common-CPProjectUseFrameworks-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectUITests/Pods-CPProject-CPProjectUITests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Common-CPProjectUseFrameworks/Pods-Common-CPProjectUseFrameworks-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 61C3645E24376AEF00C4D4E6 /* ⚙️ Run linter */ = { + 5645FD027BB938478818FE88 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -383,18 +473,21 @@ inputFileListPaths = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "⚙️ Run linter"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Common-CPProjectUseFrameworks-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\n cd ${SOURCE_ROOT}/../../\n ./tools/lint/run-linter.sh\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B5E3E8D262C8003866F38F23 /* [CP] Check Pods Manifest.lock */ = { + 8D0504D0CD7CAFBBB5B63F13 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -409,31 +502,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CPProject-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Common-CPProjectNoUseFrameworks-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - D8E080EFA5BC75427C8EA6A2 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectTests/Pods-CPProject-CPProjectTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectTests/Pods-CPProject-CPProjectTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CPProject-CPProjectTests/Pods-CPProject-CPProjectTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - E0C0BFBDF1EBCFBACB1B053D /* [CP] Check Pods Manifest.lock */ = { + D6011F89065F1B0B4B52E465 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -448,7 +524,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-CPProject-CPProjectTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-CPProjectNoUseFrameworksTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -458,60 +534,96 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 61C3638B2436318E00C4D4E6 /* Sources */ = { + 61373B2526E0E78300E0F46E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61373B5B26E0E7D100E0F46E /* ViewController.swift in Sources */, + 61373B5926E0E7C900E0F46E /* AppDelegate.swift in Sources */, + 61373B5A26E0E7CD00E0F46E /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61373B3A26E0E78500E0F46E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 61C363972436318E00C4D4E6 /* ViewController.swift in Sources */, - 61C363932436318E00C4D4E6 /* AppDelegate.swift in Sources */, - 61C363952436318E00C4D4E6 /* SceneDelegate.swift in Sources */, + 61373B5F26E0E7E400E0F46E /* CPProjectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 61C363A12436319000C4D4E6 /* Sources */ = { + 61373B4526E0E78500E0F46E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 61C363AA2436319000C4D4E6 /* CPProjectUITests.swift in Sources */, + 61373B6126E0EA2500E0F46E /* CPProjectUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 61C3644E2437568300C4D4E6 /* Sources */ = { + 61B8C2FF26E0E278006EDF53 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 61C364552437568300C4D4E6 /* CPProjectTests.swift in Sources */, + 61B8C30B26E0E278006EDF53 /* ViewController.swift in Sources */, + 61B8C30726E0E278006EDF53 /* AppDelegate.swift in Sources */, + 61B8C30926E0E278006EDF53 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C31526E0E27A006EDF53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61B8C31E26E0E27A006EDF53 /* CPProjectTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 61B8C32026E0E27A006EDF53 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61B8C32926E0E27A006EDF53 /* CPProjectUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 61C363A72436319000C4D4E6 /* PBXTargetDependency */ = { + 61373B4026E0E78500E0F46E /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 61C3638E2436318E00C4D4E6 /* CPProject */; - targetProxy = 61C363A62436319000C4D4E6 /* PBXContainerItemProxy */; + target = 61373B2826E0E78300E0F46E /* CPProjectNoUseFrameworks */; + targetProxy = 61373B3F26E0E78500E0F46E /* PBXContainerItemProxy */; }; - 61C364582437568300C4D4E6 /* PBXTargetDependency */ = { + 61373B4B26E0E78500E0F46E /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 61C3638E2436318E00C4D4E6 /* CPProject */; - targetProxy = 61C364572437568300C4D4E6 /* PBXContainerItemProxy */; + target = 61373B2826E0E78300E0F46E /* CPProjectNoUseFrameworks */; + targetProxy = 61373B4A26E0E78500E0F46E /* PBXContainerItemProxy */; + }; + 61B8C31B26E0E27A006EDF53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 61B8C30226E0E278006EDF53 /* CPProjectUseFrameworks */; + targetProxy = 61B8C31A26E0E27A006EDF53 /* PBXContainerItemProxy */; + }; + 61B8C32626E0E27A006EDF53 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 61B8C30226E0E278006EDF53 /* CPProjectUseFrameworks */; + targetProxy = 61B8C32526E0E27A006EDF53 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 61C363982436318E00C4D4E6 /* Main.storyboard */ = { + 61B8C30C26E0E278006EDF53 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - 61C363992436318E00C4D4E6 /* Base */, + 61B8C30D26E0E278006EDF53 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; - 61C3639D2436319000C4D4E6 /* LaunchScreen.storyboard */ = { + 61B8C31126E0E27A006EDF53 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( - 61C3639E2436319000C4D4E6 /* Base */, + 61B8C31226E0E27A006EDF53 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -519,9 +631,130 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 61C363AC2436319000C4D4E6 /* Debug */ = { + 61373B5126E0E78500E0F46E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 01FD54C4011C8F113E285585 /* Pods-Common-CPProjectNoUseFrameworks.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProject/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworks; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 61373B5226E0E78500E0F46E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3D97712BEE25C98C566E565A /* Pods-Common-CPProjectNoUseFrameworks.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProject/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworks; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 61373B5426E0E78500E0F46E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 51776B4903CB23F278458BB6 /* Pods-CPProjectNoUseFrameworksTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworksTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProjectNoUseFrameworks.app/CPProjectNoUseFrameworks"; + }; + name = Debug; + }; + 61373B5526E0E78500E0F46E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 02265FB44AE08BD3D3454745 /* Pods-CPProjectNoUseFrameworksTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworksTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProjectNoUseFrameworks.app/CPProjectNoUseFrameworks"; + }; + name = Release; + }; + 61373B5726E0E78500E0F46E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworksUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = CPProjectNoUseFrameworks; + }; + name = Debug; + }; + 61373B5826E0E78500E0F46E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectNoUseFrameworksUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = CPProjectNoUseFrameworks; + }; + name = Release; + }; + 61B8C32B26E0E27A006EDF53 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 61ED26E42461D29E00752D66 /* Datadog.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -571,7 +804,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -581,9 +814,8 @@ }; name = Debug; }; - 61C363AD2436319000C4D4E6 /* Release */ = { + 61B8C32C26E0E27A006EDF53 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 61ED26E42461D29E00752D66 /* Datadog.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -627,7 +859,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -637,188 +869,196 @@ }; name = Release; }; - 61C363AF2436319000C4D4E6 /* Debug */ = { + 61B8C32E26E0E27A006EDF53 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5AD49F84C2EA2EE5CB99586F /* Pods-CPProject.debug.xcconfig */; + baseConfigurationReference = 586B9607FFDD69DAD0C23F22 /* Pods-Common-CPProjectUseFrameworks.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProject/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProject; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworks; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 61C363B02436319000C4D4E6 /* Release */ = { + 61B8C32F26E0E27A006EDF53 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C7DE97263B77DC87F0FC27F0 /* Pods-CPProject.release.xcconfig */; + baseConfigurationReference = 9D6D3B3BC8427FCEF169BBEC /* Pods-Common-CPProjectUseFrameworks.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProject/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProject; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworks; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SUPPORTS_MACCATALYST = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; - 61C363B22436319000C4D4E6 /* Debug */ = { + 61B8C33126E0E27A006EDF53 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 023E9F71B1FFBEAF47AD599B /* Pods-CPProject-CPProjectUITests.debug.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CPProjectUITests/Info.plist; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProjectUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworksTests; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG COMPILING_FOR_USE_FRAMEWORKS"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = CPProject; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProjectUseFrameworks.app/CPProjectUseFrameworks"; }; name = Debug; }; - 61C363B32436319000C4D4E6 /* Release */ = { + 61B8C33226E0E27A006EDF53 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = EB80E12EA30834731C20BC88 /* Pods-CPProject-CPProjectUITests.release.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CPProjectUITests/Info.plist; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProjectUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworksTests; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = CPProject; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProjectUseFrameworks.app/CPProjectUseFrameworks"; }; name = Release; }; - 61C364592437568300C4D4E6 /* Debug */ = { + 61B8C33426E0E27A006EDF53 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6C29934101A436B996950B9C /* Pods-CPProject-CPProjectTests.debug.xcconfig */; buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CPProjectTests/Info.plist; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProjectTests; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworksUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + PROVISIONING_PROFILE = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProject.app/CPProject"; + TEST_TARGET_NAME = CPProjectUseFrameworks; }; name = Debug; }; - 61C3645A2437568300C4D4E6 /* Release */ = { + 61B8C33526E0E27A006EDF53 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0D9B145559078D7130D6AA72 /* Pods-CPProject-CPProjectTests.release.xcconfig */; buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = CPProjectTests/Info.plist; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = JKFCB4CN7C; + INFOPLIST_FILE = CPProjectUITests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.datadogqh.CPProjectTests; + PRODUCT_BUNDLE_IDENTIFIER = com.datadoghq.CPProjectUseFrameworksUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + PROVISIONING_PROFILE = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CPProject.app/CPProject"; + TEST_TARGET_NAME = CPProjectUseFrameworks; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 61C3638A2436318E00C4D4E6 /* Build configuration list for PBXProject "CPProject" */ = { + 61373B5026E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworks" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 61373B5126E0E78500E0F46E /* Debug */, + 61373B5226E0E78500E0F46E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 61373B5326E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworksTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 61373B5426E0E78500E0F46E /* Debug */, + 61373B5526E0E78500E0F46E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 61373B5626E0E78500E0F46E /* Build configuration list for PBXNativeTarget "CPProjectNoUseFrameworksUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 61373B5726E0E78500E0F46E /* Debug */, + 61373B5826E0E78500E0F46E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 61B8C2FE26E0E278006EDF53 /* Build configuration list for PBXProject "CPProject" */ = { isa = XCConfigurationList; buildConfigurations = ( - 61C363AC2436319000C4D4E6 /* Debug */, - 61C363AD2436319000C4D4E6 /* Release */, + 61B8C32B26E0E27A006EDF53 /* Debug */, + 61B8C32C26E0E27A006EDF53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 61C363AE2436319000C4D4E6 /* Build configuration list for PBXNativeTarget "CPProject" */ = { + 61B8C32D26E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworks" */ = { isa = XCConfigurationList; buildConfigurations = ( - 61C363AF2436319000C4D4E6 /* Debug */, - 61C363B02436319000C4D4E6 /* Release */, + 61B8C32E26E0E27A006EDF53 /* Debug */, + 61B8C32F26E0E27A006EDF53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 61C363B12436319000C4D4E6 /* Build configuration list for PBXNativeTarget "CPProjectUITests" */ = { + 61B8C33026E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworksTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 61C363B22436319000C4D4E6 /* Debug */, - 61C363B32436319000C4D4E6 /* Release */, + 61B8C33126E0E27A006EDF53 /* Debug */, + 61B8C33226E0E27A006EDF53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 61C3645B2437568300C4D4E6 /* Build configuration list for PBXNativeTarget "CPProjectTests" */ = { + 61B8C33326E0E27A006EDF53 /* Build configuration list for PBXNativeTarget "CPProjectUseFrameworksUITests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 61C364592437568300C4D4E6 /* Debug */, - 61C3645A2437568300C4D4E6 /* Release */, + 61B8C33426E0E27A006EDF53 /* Debug */, + 61B8C33526E0E27A006EDF53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 61C363872436318E00C4D4E6 /* Project object */; + rootObject = 61B8C2FB26E0E278006EDF53 /* Project object */; } diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata index a295d0dc32..919434a625 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectNoUseFrameworks.xcscheme b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectNoUseFrameworks.xcscheme new file mode 100644 index 0000000000..82c4219962 --- /dev/null +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectNoUseFrameworks.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectUseFrameworks.xcscheme similarity index 76% rename from dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme rename to dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectUseFrameworks.xcscheme index 14d2b3f39b..d834e8e16f 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProject.xcscheme +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/xcshareddata/xcschemes/CPProjectUseFrameworks.xcscheme @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> @@ -32,9 +32,9 @@ skipped = "NO"> @@ -42,9 +42,9 @@ skipped = "NO"> @@ -64,9 +64,9 @@ runnableDebuggingMode = "0"> @@ -81,9 +81,9 @@ runnableDebuggingMode = "0"> diff --git a/dependency-manager-tests/cocoapods/CPProject/Base.lproj/Main.storyboard b/dependency-manager-tests/cocoapods/CPProject/Base.lproj/Main.storyboard index 49690bb98b..650f1fd723 100644 --- a/dependency-manager-tests/cocoapods/CPProject/Base.lproj/Main.storyboard +++ b/dependency-manager-tests/cocoapods/CPProject/Base.lproj/Main.storyboard @@ -1,57 +1,45 @@ - + - + + - + - - - - - - - - - - - - - - - - - - - + - + + - - + + - - + + + + + + diff --git a/dependency-manager-tests/cocoapods/CPProject/Info.plist b/dependency-manager-tests/cocoapods/CPProject/Info.plist index 2a3483c0d2..5b531f7b27 100644 --- a/dependency-manager-tests/cocoapods/CPProject/Info.plist +++ b/dependency-manager-tests/cocoapods/CPProject/Info.plist @@ -39,6 +39,8 @@ + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/dependency-manager-tests/cocoapods/CPProjectTests/CPProjectTests.swift b/dependency-manager-tests/cocoapods/CPProjectTests/CPProjectTests.swift index 1a55f0036d..4f709bdb72 100644 --- a/dependency-manager-tests/cocoapods/CPProjectTests/CPProjectTests.swift +++ b/dependency-manager-tests/cocoapods/CPProjectTests/CPProjectTests.swift @@ -5,7 +5,11 @@ */ import XCTest -@testable import CPProject +#if COMPILING_FOR_USE_FRAMEWORKS +@testable import CPProjectUseFrameworks +#else +@testable import CPProjectNoUseFrameworks +#endif class CPProjectTests: XCTestCase { func testCallingLogicThatLoadsSDK() throws { diff --git a/dependency-manager-tests/cocoapods/Makefile b/dependency-manager-tests/cocoapods/Makefile index eaf79ed28d..6a27fff1e2 100644 --- a/dependency-manager-tests/cocoapods/Makefile +++ b/dependency-manager-tests/cocoapods/Makefile @@ -11,4 +11,4 @@ test: @sed "s|REMOTE_GIT_REFERENCE|${GIT_REFERENCE}|g" Podfile.src > Podfile @rm -rf Pods/ pod update - @echo "OK 👌" + @echo "👌 'pod update' OK" diff --git a/dependency-manager-tests/cocoapods/Podfile.src b/dependency-manager-tests/cocoapods/Podfile.src index 389515d1a6..5828efd2ca 100644 --- a/dependency-manager-tests/cocoapods/Podfile.src +++ b/dependency-manager-tests/cocoapods/Podfile.src @@ -1,17 +1,18 @@ platform :ios, '12.0' -use_frameworks! - -target 'CPProject' do +abstract_target 'Common' do pod 'DatadogSDK', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE pod 'DatadogSDKAlamofireExtension', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE pod 'DatadogSDKCrashReporting', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE pod 'Alamofire' - target 'CPProjectTests' do - inherit! :complete + target 'CPProjectUseFrameworks' do + use_frameworks! end - target 'CPProjectUITests' do - inherit! :complete + + target 'CPProjectNoUseFrameworks' do + target 'CPProjectNoUseFrameworksTests' do + inherit! :search_paths + end end end From 62e1f24046cd2bcb314248e42faf0878f244b7c1 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Thu, 2 Sep 2021 17:54:09 +0200 Subject: [PATCH 13/80] RUMM-1566 Fix `CPProject` deployment target for CI --- .../cocoapods/CPProject.xcodeproj/project.pbxproj | 8 ++------ dependency-manager-tests/cocoapods/Podfile.src | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj index 4d907dd298..102e2603c4 100644 --- a/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj +++ b/dependency-manager-tests/cocoapods/CPProject.xcodeproj/project.pbxproj @@ -679,7 +679,6 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProjectTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -701,7 +700,6 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProjectTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -804,7 +802,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -859,7 +857,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -916,7 +914,6 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProjectTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -938,7 +935,6 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = JKFCB4CN7C; INFOPLIST_FILE = CPProjectTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/dependency-manager-tests/cocoapods/Podfile.src b/dependency-manager-tests/cocoapods/Podfile.src index 5828efd2ca..dee2004ee1 100644 --- a/dependency-manager-tests/cocoapods/Podfile.src +++ b/dependency-manager-tests/cocoapods/Podfile.src @@ -1,4 +1,4 @@ -platform :ios, '12.0' +platform :ios, '13.0' abstract_target 'Common' do pod 'DatadogSDK', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE From 650f4c5f5a8f833373d1f6bd2dcc2c2c434304b6 Mon Sep 17 00:00:00 2001 From: Sarina Bloodgood <57639676+sarina-dd@users.noreply.github.com> Date: Wed, 1 Sep 2021 18:52:31 -0500 Subject: [PATCH 14/80] Update advanced_configuration.md Update links --- docs/rum_collection/advanced_configuration.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/rum_collection/advanced_configuration.md b/docs/rum_collection/advanced_configuration.md index 3d1eba7b87..1cd2a66811 100644 --- a/docs/rum_collection/advanced_configuration.md +++ b/docs/rum_collection/advanced_configuration.md @@ -54,7 +54,7 @@ Once the timing is sent, the timing will be accessible as `@view.custom_timings. ### Custom Actions -In addition to [tracking actions automatically][6], you can also track specific custom user actions (taps, clicks, scrolls, etc.) with `addUserAction(type:name:)` API. To manually register instantaneous RUM actions (e.g: `.tap`), on `Global.rum` use: +In addition to [tracking actions automatically][7], you can also track specific custom user actions (taps, clicks, scrolls, etc.) with `addUserAction(type:name:)` API. To manually register instantaneous RUM actions (e.g: `.tap`), on `Global.rum` use: - `.addUserAction(type:name:)` or for continuous RUM actions (e.g: `.scroll`), use: @@ -402,7 +402,6 @@ This means that even if users open your application while offline, no data is lo [3]: /real_user_monitoring/ios/data_collected [4]: #automatically-track-views [5]: https://docs.datadoghq.com/real_user_monitoring/explorer/?tab=measures#setup-facets-and-measures -[6]: #automatically-track-actions [7]: #automatically-track-network-requests [8]: /real_user_monitoring/ios/data_collected/?tab=error#error-attributes [9]: #modify-or-drop-rum-events From 18ff27cc3bae62501262b9a7e56ad43a6cd46e3f Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Fri, 3 Sep 2021 15:02:20 +0200 Subject: [PATCH 15/80] Update `advanced_configuration.md` for tracking actions automatically --- docs/rum_collection/advanced_configuration.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/rum_collection/advanced_configuration.md b/docs/rum_collection/advanced_configuration.md index 1cd2a66811..e27a1697e1 100644 --- a/docs/rum_collection/advanced_configuration.md +++ b/docs/rum_collection/advanced_configuration.md @@ -54,7 +54,7 @@ Once the timing is sent, the timing will be accessible as `@view.custom_timings. ### Custom Actions -In addition to [tracking actions automatically][7], you can also track specific custom user actions (taps, clicks, scrolls, etc.) with `addUserAction(type:name:)` API. To manually register instantaneous RUM actions (e.g: `.tap`), on `Global.rum` use: +In addition to [tracking actions automatically][6], you can also track specific custom user actions (taps, clicks, scrolls, etc.) with `addUserAction(type:name:)` API. To manually register instantaneous RUM actions (e.g: `.tap`), on `Global.rum` use: - `.addUserAction(type:name:)` or for continuous RUM actions (e.g: `.scroll`), use: @@ -239,6 +239,10 @@ class YourCustomPredicate: UIKitRUMViewsPredicate { **Note**: The SDK calls `rumView(for:)` many times while your app is running. It is recommended to keep its implementation fast and single-threaded. +### Automatically track user actions + +To automatically track user tap actions, use the `.trackUIKitActions()` option when configuring the SDK. + ### Automatically track network requests To automatically track resources (network requests) and get their timing information such as time to first byte or DNS resolution, use the `.trackURLSession()` option when configuring the SDK and set `DDURLSessionDelegate` for the `URLSession` that you want to monitor: @@ -287,7 +291,7 @@ For instance, you may want to add HTTP request and response headers to the RUM r ``` -### Automatically track RUM errors +### Automatically track errors All "error" and "critical" logs sent with `Logger` are automatically reported as RUM errors and linked to the current RUM view: ```swift @@ -402,6 +406,7 @@ This means that even if users open your application while offline, no data is lo [3]: /real_user_monitoring/ios/data_collected [4]: #automatically-track-views [5]: https://docs.datadoghq.com/real_user_monitoring/explorer/?tab=measures#setup-facets-and-measures +[6]: #automatically-track-user-actions [7]: #automatically-track-network-requests [8]: /real_user_monitoring/ios/data_collected/?tab=error#error-attributes [9]: #modify-or-drop-rum-events From fe57b508985f0bd99c1f30a1ba82e796537aa458 Mon Sep 17 00:00:00 2001 From: maxep Date: Fri, 3 Sep 2021 13:29:05 +0200 Subject: [PATCH 16/80] RUMM-1420 `additionalProperties` as [String: Encodable] --- .../Input/ObjcInterop/ObjcInteropType.swift | 6 ++-- .../SwiftToObjcInteropTypeTransformer.swift | 14 ++++++---- .../Swift/JSONToSwiftTypeTransformer.swift | 2 +- .../Input/Swift/SwiftType.swift | 7 ++++- .../Output/Printing/ObjcInteropPrinter.swift | 28 ++++++++++--------- .../Output/Printing/SwiftPrinter.swift | 14 ++++++---- .../JSONToSwiftTypeTransformerTests.swift | 4 +-- .../Output/ObjcInteropPrinterTests.swift | 4 +-- .../Output/SwiftPrinterTests.swift | 6 ++-- 9 files changed, 48 insertions(+), 37 deletions(-) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/ObjcInteropType.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/ObjcInteropType.swift index a24c0a58f3..65d0300719 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/ObjcInteropType.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/ObjcInteropType.swift @@ -149,10 +149,10 @@ internal class ObjcInteropNSString: ObjcInteropType { } internal class ObjcInteropAny: ObjcInteropType { - let swiftCodable: SwiftPrimitive + let swiftType: SwiftPrimitiveNoObjcInteropType - init(swiftCodable: SwiftPrimitive) { - self.swiftCodable = swiftCodable + init(swiftType: SwiftPrimitiveNoObjcInteropType) { + self.swiftType = swiftType } } diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/SwiftToObjcInteropTypeTransformer.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/SwiftToObjcInteropTypeTransformer.swift index af9eb12dc0..82525ca693 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/SwiftToObjcInteropTypeTransformer.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/ObjcInterop/SwiftToObjcInteropTypeTransformer.swift @@ -143,13 +143,15 @@ internal class SwiftToObjcInteropTypeTransformer { private func objcInteropType(for swiftType: SwiftType) throws -> ObjcInteropType { switch swiftType { - case _ as SwiftPrimitive, - _ as SwiftPrimitive, - _ as SwiftPrimitive, - _ as SwiftPrimitive: + case is SwiftPrimitive, + is SwiftPrimitive, + is SwiftPrimitive, + is SwiftPrimitive: return ObjcInteropNSNumber(swiftType: swiftType) - case let swiftCodable as SwiftPrimitive: - return ObjcInteropAny(swiftCodable: swiftCodable) + case let swiftCodable as SwiftCodable: + return ObjcInteropAny(swiftType: swiftCodable) + case let swiftEncodable as SwiftEncodable: + return ObjcInteropAny(swiftType: swiftEncodable) case let swiftString as SwiftPrimitive: return ObjcInteropNSString(swiftString: swiftString) case let swiftArray as SwiftArray: diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift index ea4da5f235..2e89c6b2e7 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/JSONToSwiftTypeTransformer.swift @@ -87,7 +87,7 @@ internal class JSONToSwiftTypeTransformer { name: additionalPropertyName, comment: additionalProperties.comment, type: SwiftDictionary( - value: SwiftPrimitive() + value: SwiftEncodable() ), isOptional: false, mutability: mutability, diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift index e56f71da0a..aa2fb3ae23 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Input/Swift/SwiftType.swift @@ -15,6 +15,8 @@ internal protocol SwiftPrimitiveType: SwiftType {} internal protocol SwiftPrimitiveValue {} /// An allowed default value of Swift property. internal protocol SwiftPropertyDefaultValue {} +/// An allowed value of Swift with no obj-c interoperability. +internal protocol SwiftPrimitiveNoObjcInteropType: SwiftPrimitiveType {} extension Bool: SwiftPrimitiveValue, SwiftPropertyDefaultValue {} extension Int: SwiftPrimitiveValue, SwiftPropertyDefaultValue {} @@ -23,7 +25,10 @@ extension String: SwiftPrimitiveValue, SwiftPropertyDefaultValue {} extension Double: SwiftPrimitiveValue, SwiftPropertyDefaultValue {} /// Represents `Swift.Codable` - we need to define utility type because it cannot be declared as `extension` to `Codable`. -internal struct SwiftCodable: SwiftPrimitiveValue, SwiftPropertyDefaultValue {} +internal struct SwiftCodable: SwiftPrimitiveNoObjcInteropType {} + +/// Represents `Swift.Encodable` - we need to define utility type because it cannot be declared as `extension` to `Encodable`. +internal struct SwiftEncodable: SwiftPrimitiveNoObjcInteropType {} internal struct SwiftPrimitive: SwiftPrimitiveType {} diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift index 50de1dacc6..15c36cfaec 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/ObjcInteropPrinter.swift @@ -313,10 +313,10 @@ internal class ObjcInteropPrinter: BasePrinter { private func printPrimitivePropertyWrapper(_ propertyWrapper: ObjcInteropPropertyWrapperManagingSwiftStructProperty) throws { let swiftProperty = propertyWrapper.bridgedSwiftProperty - if let swiftDictionary = swiftProperty.type as? SwiftDictionary, swiftDictionary.value is SwiftPrimitive { + if let swiftDictionary = swiftProperty.type as? SwiftDictionary, swiftDictionary.value is SwiftPrimitiveNoObjcInteropType { if swiftProperty.mutability == .mutable { throw Exception.unimplemented( - "Generating ObjcInterop for mutable `[Swift: Codable]` is not supported." + "Generating ObjcInterop for mutable property `\(swiftProperty.name)` is not supported." ) } } @@ -364,11 +364,11 @@ internal class ObjcInteropPrinter: BasePrinter { private func objcInteropTypeName(for objcType: ObjcInteropType) throws -> String { switch objcType { - case _ as ObjcInteropNSNumber: + case is ObjcInteropNSNumber: return "NSNumber" - case _ as ObjcInteropNSString: + case is ObjcInteropNSString: return "String" - case _ as ObjcInteropAny: + case is ObjcInteropAny: return "Any" case let objcArray as ObjcInteropNSArray: return "[\(try objcInteropTypeName(for: objcArray.element))]" @@ -383,13 +383,13 @@ internal class ObjcInteropPrinter: BasePrinter { private func swiftToObjcCast(for objcType: ObjcInteropType) throws -> String? { switch objcType { - case _ as ObjcInteropNSNumber: + case is ObjcInteropNSNumber: return " as NSNumber" case let nsArray as ObjcInteropNSArray where nsArray.element is ObjcInteropNSNumber: return " as [NSNumber]" case let nsDictionary as ObjcInteropNSDictionary where nsDictionary.value is ObjcInteropNSNumber: return " as [\(try objcInteropTypeName(for: nsDictionary.key)): NSNumber]" - case _ as ObjcInteropNSString: + case is ObjcInteropNSString: return nil // `String` <> `NSString` interoperability doesn't require casting case let nsArray as ObjcInteropNSArray where nsArray.element is ObjcInteropNSString: return nil // `[String]` <> `[NSString]` interoperability doesn't require casting @@ -404,19 +404,21 @@ internal class ObjcInteropPrinter: BasePrinter { private func objcToSwiftCast(for swiftType: SwiftType) throws -> String? { switch swiftType { - case _ as SwiftPrimitive: + case is SwiftPrimitive: return ".boolValue" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return ".doubleValue" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return ".intValue" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return ".int64Value" case let swiftArray as SwiftArray where swiftArray.element is SwiftPrimitive: return nil // `[String]` <> `[NSString]` interoperability doesn't require casting + case let swiftArray as SwiftArray where swiftArray.element is SwiftPrimitiveNoObjcInteropType: + return nil case let swiftDictionary as SwiftDictionary where swiftDictionary.value is SwiftPrimitive: return nil // `[Key: String]` <> `[Key: NSString]` interoperability doesn't require casting - case let swiftDictionary as SwiftDictionary where swiftDictionary.value is SwiftPrimitive: + case let swiftDictionary as SwiftDictionary where swiftDictionary.value is SwiftPrimitiveNoObjcInteropType: return nil case let swiftArray as SwiftArray: let elementCast = try objcToSwiftCast(for: swiftArray.element) @@ -427,7 +429,7 @@ internal class ObjcInteropPrinter: BasePrinter { let valueCast = try objcToSwiftCast(for: swiftDictionary.value) .unwrapOrThrow(.illegal("Cannot print `objcToSwiftCast()` for `SwiftDictionary` with values of type: \(type(of: swiftDictionary.value))")) return ".reduce(into: [:]) { $0[$1.0\(keyCast)] = $1.1\(valueCast)" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return nil // `String` <> `NSString` interoperability doesn't require casting default: throw Exception.unimplemented("Cannot print `objcToSwiftCast()` for \(type(of: swiftType)).") diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift index ce916e2425..590fb21466 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/Output/Printing/SwiftPrinter.swift @@ -324,18 +324,20 @@ public class SwiftPrinter: BasePrinter { private func typeDeclaration(_ type: SwiftType) throws -> String { switch type { - case _ as SwiftPrimitive: + case is SwiftPrimitive: return "Bool" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return "Double" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return "Int" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return "Int64" - case _ as SwiftPrimitive: + case is SwiftPrimitive: return "String" - case _ as SwiftPrimitive: + case is SwiftCodable: return "Codable" + case is SwiftEncodable: + return "Encodable" case let swiftArray as SwiftArray: return "[\(try typeDeclaration(swiftArray.element))]" case let swiftDictionary as SwiftDictionary: diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift index 6488ce2374..f4d0d07da1 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Input/JSONToSwiftTypeTransformerTests.swift @@ -248,7 +248,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { name: "propertyWithAdditionalAnyPropertiesInfo", comment: nil, type: SwiftDictionary( - value: SwiftPrimitive() + value: SwiftEncodable() ), isOptional: false, mutability: .mutableInternally, @@ -331,7 +331,7 @@ final class JSONToSwiftTypeTransformerTests: XCTestCase { name: "barInfo", comment: "Additional properties of `bar`.", type: SwiftDictionary( - value: SwiftPrimitive() + value: SwiftEncodable() ), isOptional: false, mutability: .mutableInternally, diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift index 1da407b898..5892a9e048 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/ObjcInteropPrinterTests.swift @@ -1245,13 +1245,13 @@ final class ObjcInteropPrinterTests: XCTestCase { properties: [ .mock( propertyName: "immutableCodables", - type: SwiftDictionary(value: SwiftPrimitive()), + type: SwiftDictionary(value: SwiftCodable()), isOptional: false, mutability: .immutable ), .mock( propertyName: "optionalImmutableCodables", - type: SwiftDictionary(value: SwiftPrimitive()), + type: SwiftDictionary(value: SwiftCodable()), isOptional: true, mutability: .immutable ), diff --git a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift index 2f9c6c3847..9ed05e0ea0 100644 --- a/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift +++ b/tools/rum-models-generator/Tests/rum-models-generator-coreTests/Output/SwiftPrinterTests.swift @@ -264,7 +264,7 @@ final class SwiftPrinterTests: XCTestCase { name: "context", comment: nil, type: SwiftDictionary( - value: SwiftPrimitive() + value: SwiftCodable() ), isOptional: false, mutability: .immutable, @@ -339,7 +339,7 @@ final class SwiftPrinterTests: XCTestCase { name: "context", comment: nil, type: SwiftDictionary( - value: SwiftPrimitive() + value: SwiftEncodable() ), isOptional: false, mutability: .mutableInternally, @@ -367,7 +367,7 @@ final class SwiftPrinterTests: XCTestCase { public struct Foo: Codable { public let property1: Int - public internal(set) var context: [String: Codable] + public internal(set) var context: [String: Encodable] public var property2: Bool? From 0df795708b6d177f572ce9accb82150c3993d309 Mon Sep 17 00:00:00 2001 From: maxep Date: Fri, 3 Sep 2021 16:38:10 +0200 Subject: [PATCH 17/80] RUMM-1420 generate models --- Sources/Datadog/RUM/DataModels/RUMDataModels.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift index 1eb379d531..65d319b5cd 100644 --- a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift +++ b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift @@ -1241,7 +1241,7 @@ public struct RUMConnectivity: Codable { /// User provided context public struct RUMEventAttributes: Codable { - public internal(set) var contextInfo: [String: Codable] + public internal(set) var contextInfo: [String: Encodable] struct DynamicCodingKey: CodingKey { var stringValue: String @@ -1287,7 +1287,7 @@ public struct RUMUser: Codable { /// Name of the user public let name: String? - public internal(set) var usrInfo: [String: Codable] + public internal(set) var usrInfo: [String: Encodable] enum StaticCodingKeys: String, CodingKey { case email = "email" From a10ebb0fbda64231d65b1a3ffbf54f2784d90f34 Mon Sep 17 00:00:00 2001 From: maxep Date: Thu, 2 Sep 2021 17:45:38 +0200 Subject: [PATCH 18/80] RUMM-1420 Remove `attributes` and `userInfoAttributes` property from `RUMEvent` --- .../CrashContext/CrashContext.swift | 26 +--- .../CrashReportingWithRUMIntegration.swift | 19 +-- .../RUM/RUMEvent/RUMEventBuilder.swift | 16 +- .../RUM/RUMEvent/RUMEventEncoder.swift | 69 ++++---- .../RUM/RUMEvent/RUMEventSanitizer.swift | 54 +++++-- .../CrashContext/CrashContextTests.swift | 4 +- ...rashReportingWithRUMIntegrationTests.swift | 10 +- .../Datadog/Mocks/RUMFeatureMocks.swift | 31 ++-- .../RUM/RUMEvent/RUMEventBuilderTests.swift | 4 +- .../RUM/RUMEvent/RUMEventSanitizerTests.swift | 147 +++++++++--------- .../RUMEventFileOutputTests.swift | 10 +- .../Scopes/RUMResourceScopeTests.swift | 8 +- .../Scopes/RUMUserActionScopeTests.swift | 2 +- .../RUMMonitor/Scopes/RUMViewScopeTests.swift | 8 +- .../RUM/Scrubbing/RUMEventsMapperTests.swift | 55 ++----- .../Datadog/RUMMonitorTests.swift | 25 ++- 16 files changed, 235 insertions(+), 253 deletions(-) diff --git a/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift b/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift index fa070a42f9..b1b9c68690 100644 --- a/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift +++ b/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift @@ -150,47 +150,35 @@ private struct CodableUserInfo: Codable { private struct CodableRUMViewEvent: Codable { private let model: RUMViewEvent - private let attributes: [String: Encodable] - private let userInfoAttributes: [String: Encodable] + private let errorAttributes: [AttributeKey: AttributeValue]? init(from managedValue: RUMEvent) { self.model = managedValue.model - self.attributes = managedValue.attributes - self.userInfoAttributes = managedValue.userInfoAttributes + self.errorAttributes = managedValue.errorAttributes } var managedValue: RUMEvent { - return .init( - model: model, - attributes: attributes, - userInfoAttributes: userInfoAttributes - ) + return .init(model: model, errorAttributes: errorAttributes) } // MARK: - Codable enum CodingKeys: String, CodingKey { case model = "mdl" - case attributes = "att" - case userInfoAttributes = "uia" + case errorAttributes = "ea" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) - self.model = try container.decode(RUMViewEvent.self, forKey: .model) - self.attributes = try container.decode([String: CodableValue].self, forKey: .attributes) - self.userInfoAttributes = try container.decode([String: CodableValue].self, forKey: .userInfoAttributes) + self.errorAttributes = try container.decodeIfPresent([String: CodableValue].self, forKey: .errorAttributes) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - let encodedAttributes = attributes.mapValues { CodableValue($0) } - let encodedUserInfoAttributes = userInfoAttributes.mapValues { CodableValue($0) } - try container.encode(model, forKey: .model) - try container.encode(encodedAttributes, forKey: .attributes) - try container.encode(encodedUserInfoAttributes, forKey: .userInfoAttributes) + let error = errorAttributes?.mapValues { CodableValue($0) } + try container.encode(error, forKey: .errorAttributes) } } diff --git a/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift b/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift index a4c55b64b1..6e034ac40f 100644 --- a/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift +++ b/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift @@ -88,11 +88,11 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { let errorMessage = crashReport.message let errorStackTrace = crashReport.stack - var errorEventAttributes = lastRUMViewEvent.attributes - errorEventAttributes[DDError.threads] = crashReport.threads - errorEventAttributes[DDError.binaryImages] = crashReport.binaryImages - errorEventAttributes[DDError.meta] = crashReport.meta - errorEventAttributes[DDError.wasTruncated] = crashReport.wasTruncated + var errorAttributes = lastRUMViewEvent.errorAttributes ?? [:] + errorAttributes[DDError.threads] = crashReport.threads + errorAttributes[DDError.binaryImages] = crashReport.binaryImages + errorAttributes[DDError.meta] = crashReport.meta + errorAttributes[DDError.wasTruncated] = crashReport.wasTruncated let rumError = RUMErrorEvent( dd: .init( @@ -131,8 +131,7 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { return RUMEvent( model: rumError, - attributes: errorEventAttributes, - userInfoAttributes: lastRUMViewEvent.userInfoAttributes + errorAttributes: errorAttributes ) } @@ -185,10 +184,6 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { ) ) - return RUMEvent( - model: rumView, - attributes: rumViewEvent.attributes, - userInfoAttributes: rumViewEvent.userInfoAttributes - ) + return RUMEvent(model: rumView) } } diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift index a7f9372d92..366d75640b 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift @@ -22,11 +22,17 @@ internal class RUMEventBuilder { with model: DM, attributes: [String: Encodable] ) -> RUMEvent? { - let event = RUMEvent( - model: model, - attributes: attributes, - userInfoAttributes: userInfoProvider.value.extraInfo - ) + var model = model + + if !attributes.isEmpty { + model.context = RUMEventAttributes(contextInfo: attributes) + } + + if !userInfoProvider.value.extraInfo.isEmpty { + model.usr = RUMUser(email: model.usr?.email, id: model.usr?.id, name: model.usr?.name, usrInfo: userInfoProvider.value.extraInfo) + } + + let event = RUMEvent(model: model) let mappedEvent = eventsMapper.map(event: event) return mappedEvent } diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift index a897492af9..05c3454a40 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift @@ -8,22 +8,41 @@ import Foundation /// `Encodable` representation of RUM event. /// Mutable properties are subject of sanitization or data scrubbing. -internal struct RUMEvent: Encodable { +/// /// TODO: RUMM-1463 Remove `errorAttributes` property once new error format is managed through `RUMDataModels` +/// TODO: RUMM-1584 - Remove `RUMEvent` container. +internal struct RUMEvent: Encodable where DM: RUMDataModel, DM: RUMSanitizableEvent { /// The actual RUM event model created by `RUMMonitor` var model: DM - /// Custom attributes set by the user - var attributes: [String: Encodable] - var userInfoAttributes: [String: Encodable] + /// Error attributes. + let errorAttributes: [String: Encodable]? + + /// Creates a RUM Event object object based on the given sanitizable model. + /// + /// The error attributes keys must be prefixed by `error.*`. + /// + /// - Parameters: + /// - model: The sanitizable event model. + /// - errorAttributes: The optional error attributes. + init(model: DM, errorAttributes: [String: Encodable]? = nil) { + self.model = model + self.errorAttributes = errorAttributes + } func encode(to encoder: Encoder) throws { - let sanitizedEvent = RUMEventSanitizer().sanitize(event: self) - try RUMEventEncoder().encode(sanitizedEvent, to: encoder) + // Encode attributes + var container = encoder.container(keyedBy: DynamicCodingKey.self) + + // TODO: RUMM-1463 Remove this `errorAttributes` property once new error format is managed through `RUMDataModels` + try errorAttributes?.forEach { attribute in + try container.encode(CodableValue(attribute.value), forKey: DynamicCodingKey(attribute.key)) + } + + // Encode the sanitized `RUMDataModel`. + let sanitizedModel = RUMEventSanitizer().sanitize(event: model) + try sanitizedModel.encode(to: encoder) } -} -/// Encodes `RUMEvent` to given encoder. -internal struct RUMEventEncoder { /// Coding keys for dynamic `RUMEvent` attributes specified by user. private struct DynamicCodingKey: CodingKey { var stringValue: String @@ -32,23 +51,21 @@ internal struct RUMEventEncoder { init?(intValue: Int) { return nil } init(_ string: String) { self.stringValue = string } } +} - func encode(_ event: RUMEvent, to encoder: Encoder) throws { - // Encode attributes - var attributesContainer = encoder.container(keyedBy: DynamicCodingKey.self) - try event.attributes.forEach { attributeName, attributeValue in - // TODO: RUMM-1463 Remove this condition once new error format is managed through `RUMDataModels` - if attributeName == DDError.threads || attributeName == DDError.binaryImages || attributeName == DDError.meta || attributeName == DDError.wasTruncated { - try attributesContainer.encode(CodableValue(attributeValue), forKey: DynamicCodingKey(attributeName)) - } else { - try attributesContainer.encode(CodableValue(attributeValue), forKey: DynamicCodingKey("context.\(attributeName)")) - } - } - try event.userInfoAttributes.forEach { attributeName, attributeValue in - try attributesContainer.encode(CodableValue(attributeValue), forKey: DynamicCodingKey("usr.\(attributeName)")) - } +/// Constraint on RUM event types that require sanitization before encoding. +internal protocol RUMSanitizableEvent { + /// Mutable user property. + var usr: RUMUser? { get set } - // Encode `RUMDataModel` - try event.model.encode(to: encoder) - } + /// Mutable event contect. + var context: RUMEventAttributes? { get set } } + +extension RUMViewEvent: RUMSanitizableEvent {} + +extension RUMActionEvent: RUMSanitizableEvent {} + +extension RUMResourceEvent: RUMSanitizableEvent {} + +extension RUMErrorEvent: RUMSanitizableEvent {} diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventSanitizer.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventSanitizer.swift index be3f042ad3..60411a5cc2 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventSanitizer.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventSanitizer.swift @@ -10,26 +10,46 @@ import Foundation internal struct RUMEventSanitizer { private let attributesSanitizer = AttributesSanitizer(featureName: "RUM Event") - func sanitize(event: RUMEvent) -> RUMEvent { - // Sanitize attribute names - var sanitizedUserExtraInfo = attributesSanitizer.sanitizeKeys(for: event.userInfoAttributes, prefixLevels: 1) - var sanitizedAttributes = attributesSanitizer.sanitizeKeys(for: event.attributes, prefixLevels: 1) + func sanitize(event: Event) -> Event where Event: RUMSanitizableEvent { + var event = event // Limit to max number of attributes. // If any attributes need to be removed, we first reduce number of // event attributes, then user info extra attributes. - sanitizedUserExtraInfo = attributesSanitizer.limitNumberOf( - attributes: sanitizedUserExtraInfo, - to: AttributesSanitizer.Constraints.maxNumberOfAttributes - ) - sanitizedAttributes = attributesSanitizer.limitNumberOf( - attributes: sanitizedAttributes, - to: AttributesSanitizer.Constraints.maxNumberOfAttributes - sanitizedUserExtraInfo.count - ) - - var sanitizedEvent = event - sanitizedEvent.attributes = sanitizedAttributes - sanitizedEvent.userInfoAttributes = sanitizedUserExtraInfo - return sanitizedEvent + var limit = AttributesSanitizer.Constraints.maxNumberOfAttributes + event.usr = sanitize(usr: event.usr, limit: &limit) + event.context = sanitize(context: event.context, limit: &limit) + + return event + } + + private func sanitize(usr: RUMUser?, limit: inout Int) -> RUMUser? { + guard var usr = usr else { + return nil + } + + // Sanitize attribute names + let attributes = attributesSanitizer.sanitizeKeys(for: usr.usrInfo, prefixLevels: 1) + + // Limit to max number of attributes. + usr.usrInfo = attributesSanitizer.limitNumberOf(attributes: attributes, to: limit) + + limit -= usr.usrInfo.count + return usr + } + + private func sanitize(context: RUMEventAttributes?, limit: inout Int) -> RUMEventAttributes? { + guard var context = context else { + return nil + } + + // Sanitize attribute names + let attributes = attributesSanitizer.sanitizeKeys(for: context.contextInfo, prefixLevels: 1) + + // Limit to max number of attributes. + context.contextInfo = attributesSanitizer.limitNumberOf(attributes: attributes, to: limit) + + limit -= context.contextInfo.count + return context } } diff --git a/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextTests.swift b/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextTests.swift index fb599a8634..08e3f98905 100644 --- a/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextTests.swift +++ b/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextTests.swift @@ -28,9 +28,7 @@ class CrashContextTests: XCTestCase { func testGivenContextWithLastRUMViewEventSet_whenItGetsEncoded_thenTheValueIsPreservedAfterDecoding() throws { let randomRUMViewEvent = RUMEvent( - model: RUMViewEvent.mockRandom(), - attributes: mockRandomAttributes(), - userInfoAttributes: mockRandomAttributes() + model: RUMViewEvent.mockRandom() ) // Given diff --git a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift index 9237a1372c..80c3472dc4 100644 --- a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift +++ b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift @@ -115,7 +115,7 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { let crashReport: DDCrashReport = .mockWith(date: crashDate) let crashContext: CrashContext = .mockWith( lastTrackingConsent: .granted, - lastRUMViewEvent: .mockRandomWith(model: lastRUMViewEvent) + lastRUMViewEvent: RUMEvent(model: lastRUMViewEvent) ) // When @@ -245,9 +245,9 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { """ ) XCTAssertEqual(sendRUMErrorEvent.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") - XCTAssertEqual(sendRUMEvent.attributes[DDError.threads] as? [DDCrashReport.Thread], crashReport.threads) - XCTAssertEqual(sendRUMEvent.attributes[DDError.binaryImages] as? [DDCrashReport.BinaryImage], crashReport.binaryImages) - XCTAssertEqual(sendRUMEvent.attributes[DDError.meta] as? DDCrashReport.Meta, crashReport.meta) - XCTAssertEqual(sendRUMEvent.attributes[DDError.wasTruncated] as? Bool, crashReport.wasTruncated) + XCTAssertEqual(sendRUMEvent.errorAttributes?[DDError.threads] as? [DDCrashReport.Thread], crashReport.threads) + XCTAssertEqual(sendRUMEvent.errorAttributes?[DDError.binaryImages] as? [DDCrashReport.BinaryImage], crashReport.binaryImages) + XCTAssertEqual(sendRUMEvent.errorAttributes?[DDError.meta] as? DDCrashReport.Meta, crashReport.meta) + XCTAssertEqual(sendRUMEvent.errorAttributes?[DDError.wasTruncated] as? Bool, crashReport.wasTruncated) } } diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index f7b5215930..8025649856 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -88,31 +88,21 @@ extension RUMResourceType { // MARK: - RUMDataModel Mocks -struct RUMDataModelMock: RUMDataModel, Equatable { +struct RUMDataModelMock: RUMDataModel, RUMSanitizableEvent, EquatableInTests { let attribute: String + var usr: RUMUser? + var context: RUMEventAttributes? } // MARK: - Component Mocks extension RUMEvent: AnyMockable where DM == RUMViewEvent { static func mockAny() -> RUMEvent { - return .mockWith(model: RUMViewEvent.mockRandom()) + return RUMEvent(model: RUMViewEvent.mockRandom()) } } extension RUMEvent { - static func mockWith( - model: DM, - attributes: [String: Encodable] = [:], - userInfoAttributes: [String: Encodable] = [:] - ) -> RUMEvent { - return RUMEvent( - model: model, - attributes: attributes, - userInfoAttributes: userInfoAttributes - ) - } - static func mockRandomWith(model: DM) -> RUMEvent { func randomAttributes(prefixed prefix: String) -> [String: Encodable] { var attributes: [String: String] = [:] @@ -120,11 +110,16 @@ extension RUMEvent { return attributes } - return RUMEvent( - model: model, - attributes: randomAttributes(prefixed: "event-attribute"), - userInfoAttributes: randomAttributes(prefixed: "user-attribute") + var model = model + model.context = RUMEventAttributes(contextInfo: randomAttributes(prefixed: "event-attribute")) + model.usr = RUMUser( + email: model.usr?.email, + id: model.usr?.id, + name: model.usr?.name, + usrInfo: randomAttributes(prefixed: "user-attribute") ) + + return RUMEvent(model: model) } } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift index 320018f9c8..09ed959bae 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift @@ -18,8 +18,8 @@ class RUMEventBuilderTests: XCTestCase { ) XCTAssertEqual(event.model.attribute, "foo") - XCTAssertEqual((event.attributes as? [String: String])?["foo"], "bar") - XCTAssertEqual((event.attributes as? [String: String])?["fizz"], "buzz") + XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["foo"], "bar") + XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["fizz"], "buzz") } func testGivenEventBuilderWithEventMapper_whenEventIsModified_itBuildsModifiedEvent() throws { diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift index 5aa2535d5f..9d27117dab 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift @@ -14,78 +14,77 @@ class RUMEventSanitizerTests: XCTestCase { private let errorEvent: RUMErrorEvent = .mockRandom() func testWhenAttributeNameExceeds10NestedLevels_itIsEscapedByUnderscore() { - func test(model: DM) { - let event = RUMEvent( - model: model, - attributes: [ - "attribute-one": mockValue(), - "attribute-one.two": mockValue(), - "attribute-one.two.three": mockValue(), - "attribute-one.two.three.four": mockValue(), - "attribute-one.two.three.four.five": mockValue(), - "attribute-one.two.three.four.five.six": mockValue(), - "attribute-one.two.three.four.five.six.seven": mockValue(), - "attribute-one.two.three.four.five.six.seven.eight": mockValue(), - "attribute-one.two.three.four.five.six.seven.eight.nine": mockValue(), - "attribute-one.two.three.four.five.six.seven.eight.nine.ten": mockValue(), - "attribute-one.two.three.four.five.six.seven.eight.nine.ten.eleven": mockValue(), - "attribute-one.two.three.four.five.six.seven.eight.nine.ten.eleven.twelve": mockValue(), - ], - userInfoAttributes: [ - "user-info-one": mockValue(), - "user-info-one.two": mockValue(), - "user-info-one.two.three": mockValue(), - "user-info-one.two.three.four": mockValue(), - "user-info-one.two.three.four.five": mockValue(), - "user-info-one.two.three.four.five.six": mockValue(), - "user-info-one.two.three.four.five.six.seven": mockValue(), - "user-info-one.two.three.four.five.six.seven.eight": mockValue(), - "user-info-one.two.three.four.five.six.seven.eight.nine": mockValue(), - "user-info-one.two.three.four.five.six.seven.eight.nine.ten": mockValue(), - "user-info-one.two.three.four.five.six.seven.eight.nine.ten.eleven": mockValue(), - "user-info-one.two.three.four.five.six.seven.eight.nine.ten.eleven.twelve": mockValue(), - ] - ) + func test(event: Event) where Event: RUMSanitizableEvent { + var event = event + event.context?.contextInfo = [ + "attribute-one": mockValue(), + "attribute-one.two": mockValue(), + "attribute-one.two.three": mockValue(), + "attribute-one.two.three.four": mockValue(), + "attribute-one.two.three.four.five": mockValue(), + "attribute-one.two.three.four.five.six": mockValue(), + "attribute-one.two.three.four.five.six.seven": mockValue(), + "attribute-one.two.three.four.five.six.seven.eight": mockValue(), + "attribute-one.two.three.four.five.six.seven.eight.nine": mockValue(), + "attribute-one.two.three.four.five.six.seven.eight.nine.ten": mockValue(), + "attribute-one.two.three.four.five.six.seven.eight.nine.ten.eleven": mockValue(), + "attribute-one.two.three.four.five.six.seven.eight.nine.ten.eleven.twelve": mockValue(), + ] + + event.usr?.usrInfo = [ + "user-info-one": mockValue(), + "user-info-one.two": mockValue(), + "user-info-one.two.three": mockValue(), + "user-info-one.two.three.four": mockValue(), + "user-info-one.two.three.four.five": mockValue(), + "user-info-one.two.three.four.five.six": mockValue(), + "user-info-one.two.three.four.five.six.seven": mockValue(), + "user-info-one.two.three.four.five.six.seven.eight": mockValue(), + "user-info-one.two.three.four.five.six.seven.eight.nine": mockValue(), + "user-info-one.two.three.four.five.six.seven.eight.nine.ten": mockValue(), + "user-info-one.two.three.four.five.six.seven.eight.nine.ten.eleven": mockValue(), + "user-info-one.two.three.four.five.six.seven.eight.nine.ten.eleven.twelve": mockValue(), + ] // When let sanitized = RUMEventSanitizer().sanitize(event: event) // Then - XCTAssertEqual(sanitized.attributes.count, 12) - XCTAssertNotNil(sanitized.attributes["attribute-one"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six.seven"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six.seven.eight"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six.seven.eight.nine_ten"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six.seven.eight.nine_ten_eleven"]) - XCTAssertNotNil(sanitized.attributes["attribute-one.two.three.four.five.six.seven.eight.nine_ten_eleven_twelve"]) - - XCTAssertEqual(sanitized.userInfoAttributes.count, 12) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six.seven"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six.seven.eight"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six.seven.eight.nine_ten"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six.seven.eight.nine_ten_eleven"]) - XCTAssertNotNil(sanitized.userInfoAttributes["user-info-one.two.three.four.five.six.seven.eight.nine_ten_eleven_twelve"]) + XCTAssertEqual(sanitized.context?.contextInfo.count, 12) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six.seven"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six.seven.eight"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six.seven.eight.nine_ten"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six.seven.eight.nine_ten_eleven"]) + XCTAssertNotNil(sanitized.context?.contextInfo["attribute-one.two.three.four.five.six.seven.eight.nine_ten_eleven_twelve"]) + + XCTAssertEqual(sanitized.usr?.usrInfo.count, 12) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six.seven"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six.seven.eight"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six.seven.eight.nine_ten"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six.seven.eight.nine_ten_eleven"]) + XCTAssertNotNil(sanitized.usr?.usrInfo["user-info-one.two.three.four.five.six.seven.eight.nine_ten_eleven_twelve"]) } - test(model: viewEvent) - test(model: resourceEvent) - test(model: actionEvent) - test(model: errorEvent) + test(event: viewEvent) + test(event: resourceEvent) + test(event: actionEvent) + test(event: errorEvent) } func testWhenNumberOfAttributesExceedsLimit_itDropsExtraOnes() { - func test(model: DM) { + func test(event: Event) where Event: RUMSanitizableEvent { let oneHalfOfTheLimit = Int(Double(AttributesSanitizer.Constraints.maxNumberOfAttributes) * 0.5) let twiceTheLimit = AttributesSanitizer.Constraints.maxNumberOfAttributes * 2 @@ -99,31 +98,29 @@ class RUMEventSanitizerTests: XCTestCase { ("user-info-\(index)", mockValue()) } - let event = RUMEvent( - model: model, - attributes: Dictionary(uniqueKeysWithValues: mockAttributes), - userInfoAttributes: Dictionary(uniqueKeysWithValues: mockUserInfoAttributes) - ) + var event = event + event.context?.contextInfo = Dictionary(uniqueKeysWithValues: mockAttributes) + event.usr?.usrInfo = Dictionary(uniqueKeysWithValues: mockUserInfoAttributes) // When let sanitized = RUMEventSanitizer().sanitize(event: event) // Then var remaining = AttributesSanitizer.Constraints.maxNumberOfAttributes - let expectedSanitizedUserInfo = min(sanitized.userInfoAttributes.count, remaining) + let expectedSanitizedUserInfo = min(sanitized.usr!.usrInfo.count , remaining) remaining -= expectedSanitizedUserInfo - let expectedSanitizedAttrs = min(sanitized.attributes.count, remaining) + let expectedSanitizedAttrs = min(sanitized.context!.contextInfo.count, remaining) remaining -= expectedSanitizedAttrs XCTAssertGreaterThanOrEqual(remaining, 0) - XCTAssertEqual(sanitized.userInfoAttributes.count, expectedSanitizedUserInfo, "If number of attributes needs to be limited, `userInfoAttributes` are removed second") - XCTAssertEqual(sanitized.attributes.count, expectedSanitizedAttrs, "If number of attributes needs to be limited, `attributes` are removed first.") + XCTAssertEqual(sanitized.usr?.usrInfo.count, expectedSanitizedUserInfo, "If number of attributes needs to be limited, `userInfoAttributes` are removed second") + XCTAssertEqual(sanitized.context?.contextInfo.count, expectedSanitizedAttrs, "If number of attributes needs to be limited, `attributes` are removed first.") } - test(model: viewEvent) - test(model: resourceEvent) - test(model: actionEvent) - test(model: errorEvent) + test(event: viewEvent) + test(event: resourceEvent) + test(event: actionEvent) + test(event: errorEvent) } // MARK: - Private diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift index 723a189d53..710b1b952e 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift @@ -35,7 +35,7 @@ class RUMEventFileOutputTests: XCTestCase { ) ) - let dataModel1 = RUMDataModelMock(attribute: "foo") + let dataModel1 = RUMDataModelMock(attribute: "foo", context: RUMEventAttributes(contextInfo: ["custom.attribute": "value"])) let dataModel2 = RUMDataModelMock(attribute: "bar") let event1 = try XCTUnwrap(builder.createRUMEvent(with: dataModel1, attributes: ["custom.attribute": "value"])) let event2 = try XCTUnwrap(builder.createRUMEvent(with: dataModel2, attributes: [:])) @@ -49,7 +49,9 @@ class RUMEventFileOutputTests: XCTestCase { let event1FileName = fileNameFrom(fileCreationDate: .mockDecember15th2019At10AMUTC()) let event1Data = try temporaryDirectory.file(named: event1FileName).read() let event1Matcher = try RUMEventMatcher.fromJSONObjectData(event1Data) - XCTAssertEqual(try event1Matcher.model(), dataModel1) + + let expectedDatamodel1 = RUMDataModelMock(attribute: "foo", context: RUMEventAttributes(contextInfo: ["custom.attribute": CodableValue("value")])) + XCTAssertEqual(try event1Matcher.model(), expectedDatamodel1) let event2FileName = fileNameFrom(fileCreationDate: .mockDecember15th2019At10AMUTC(addingTimeInterval: 1)) let event2Data = try temporaryDirectory.file(named: event2FileName).read() @@ -62,7 +64,9 @@ class RUMEventFileOutputTests: XCTestCase { jsonString: """ { "attribute": "foo", - "context.custom.attribute": "value" + "context": { + "custom.attribute": "value" + } } """ ) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift index efef193ec4..5874098d75 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift @@ -94,7 +94,7 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertNil(event.model.resource.firstByte) XCTAssertNil(event.model.resource.download) XCTAssertEqual(try XCTUnwrap(event.model.action?.id), context.activeUserActionID?.toRUMDataFormat) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.model.dd.traceId, "100") XCTAssertEqual(event.model.dd.spanId, "200") XCTAssertEqual(event.model.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") @@ -158,7 +158,7 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertNil(event.model.resource.firstByte) XCTAssertNil(event.model.resource.download) XCTAssertEqual(try XCTUnwrap(event.model.action?.id), context.activeUserActionID?.toRUMDataFormat) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.model.dd.traceId, "100") XCTAssertEqual(event.model.dd.spanId, "200") } @@ -213,7 +213,7 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertEqual(event.model.error.resource?.statusCode, 500) XCTAssertEqual(event.model.error.resource?.url, "https://foo.com/resource/1") XCTAssertEqual(try XCTUnwrap(event.model.action?.id), context.activeUserActionID?.toRUMDataFormat) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.model.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") } @@ -320,7 +320,7 @@ class RUMResourceScopeTests: XCTestCase { XCTAssertEqual(event.model.resource.download?.start, 9_000_000_000) XCTAssertEqual(event.model.resource.download?.duration, 1_000_000_000) XCTAssertEqual(try XCTUnwrap(event.model.action?.id), context.activeUserActionID?.toRUMDataFormat) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertNil(event.model.dd.traceId) XCTAssertNil(event.model.dd.spanId) } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift index cfe03557b0..09b5dd3f86 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift @@ -101,7 +101,7 @@ class RUMUserActionScopeTests: XCTestCase { XCTAssertEqual(event.model.action.loadingTime, 1_000_000_000) XCTAssertEqual(event.model.action.resource?.count, 0) XCTAssertEqual(event.model.action.error?.count, 0) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) } func testWhenContinuousUserActionExpires_itSendsActionEvent() throws { diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index 5b34e3d7ed..b87e538961 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -129,7 +129,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(event.model.view.error.count, 0) XCTAssertEqual(event.model.view.resource.count, 0) XCTAssertEqual(event.model.dd.documentVersion, 1) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(event.model.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") } @@ -167,7 +167,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(event.model.view.error.count, 0) XCTAssertEqual(event.model.view.resource.count, 0) XCTAssertEqual(event.model.dd.documentVersion, 1) - XCTAssertEqual(event.attributes as? [String: String], ["foo": "bar 2", "fizz": "buzz"]) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar 2", "fizz": "buzz"]) } func testWhenViewIsStopped_itSendsViewUpdateEvent_andEndsTheScope() throws { @@ -217,7 +217,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(event.model.view.error.count, 0) XCTAssertEqual(event.model.view.resource.count, 0) XCTAssertEqual(event.model.dd.documentVersion, 2) - XCTAssertTrue(event.attributes.isEmpty) + XCTAssertNil(event.model.context) } func testWhenAnotherViewIsStarted_itEndsTheScope() throws { @@ -582,7 +582,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertNil(error.model.error.isCrash) XCTAssertNil(error.model.error.resource) XCTAssertNil(error.model.action) - XCTAssertEqual(error.attributes as? [String: String], ["foo": "bar"]) + XCTAssertEqual(error.model.context?.contextInfo as? [String: String], ["foo": "bar"]) XCTAssertEqual(error.model.dd.session?.plan, .plan1, "All RUM events should use RUM Lite plan") let viewUpdate = try XCTUnwrap(output.recordedEvents(ofType: RUMEvent.self).last) diff --git a/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift b/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift index 23659761db..bcd977fcc7 100644 --- a/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift @@ -42,10 +42,10 @@ class RUMEventsMapperTests: XCTestCase { ) // When - let mappedViewEvent = mapper.map(event: RUMEvent.mockWith(model: originalViewEvent))?.model - let mappedErrorEvent = mapper.map(event: RUMEvent.mockWith(model: originalErrorEvent))?.model - let mappedResourceEvent = mapper.map(event: RUMEvent.mockWith(model: originalResourceEvent))?.model - let mappedActionEvent = mapper.map(event: RUMEvent.mockWith(model: originalActionEvent))?.model + let mappedViewEvent = mapper.map(event: RUMEvent(model: originalViewEvent))?.model + let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model + let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model + let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model // Then XCTAssertEqual(try XCTUnwrap(mappedViewEvent), modifiedViewEvent, "Mapper should return modified event.") @@ -54,39 +54,6 @@ class RUMEventsMapperTests: XCTestCase { XCTAssertEqual(try XCTUnwrap(mappedActionEvent), modifiedActionEvent, "Mapper should return modified event.") } - func testGivenMappersEnabled_whenModifyingEvents_itDoesNotModifyCustomAttributes() throws { - // Given - let mapper = RUMEventsMapper( - viewEventMapper: { _ in .mockRandom() }, - errorEventMapper: { _ in .mockRandom() }, - resourceEventMapper: { _ in .mockRandom() }, - actionEventMapper: { _ in .mockRandom() } - ) - - // When - let rumEvent1: RUMEvent = .mockRandomWith(model: .mockRandom()) - let rumEvent2: RUMEvent = .mockRandomWith(model: .mockRandom()) - let rumEvent3: RUMEvent = .mockRandomWith(model: .mockRandom()) - let rumEvent4: RUMEvent = .mockRandomWith(model: .mockRandom()) - let mappedRUMEvent1 = try XCTUnwrap(mapper.map(event: rumEvent1)) - let mappedRUMEvent2 = try XCTUnwrap(mapper.map(event: rumEvent2)) - let mappedRUMEvent3 = try XCTUnwrap(mapper.map(event: rumEvent3)) - let mappedRUMEvent4 = try XCTUnwrap(mapper.map(event: rumEvent4)) - - // Then - XCTAssertEqual(rumEvent1.attributes as! [String: String], mappedRUMEvent1.attributes as! [String: String]) - XCTAssertEqual(rumEvent1.userInfoAttributes as! [String: String], mappedRUMEvent1.userInfoAttributes as! [String: String]) - - XCTAssertEqual(rumEvent2.attributes as! [String: String], mappedRUMEvent2.attributes as! [String: String]) - XCTAssertEqual(rumEvent2.userInfoAttributes as! [String: String], mappedRUMEvent2.userInfoAttributes as! [String: String]) - - XCTAssertEqual(rumEvent3.attributes as! [String: String], mappedRUMEvent3.attributes as! [String: String]) - XCTAssertEqual(rumEvent3.userInfoAttributes as! [String: String], mappedRUMEvent3.userInfoAttributes as! [String: String]) - - XCTAssertEqual(rumEvent4.attributes as! [String: String], mappedRUMEvent4.attributes as! [String: String]) - XCTAssertEqual(rumEvent4.userInfoAttributes as! [String: String], mappedRUMEvent4.userInfoAttributes as! [String: String]) - } - func testGivenMappersEnabled_whenDroppingEvents_itReturnsNil() { let originalErrorEvent: RUMErrorEvent = .mockRandom() let originalResourceEvent: RUMResourceEvent = .mockRandom() @@ -110,9 +77,9 @@ class RUMEventsMapperTests: XCTestCase { ) // When - let mappedErrorEvent = mapper.map(event: RUMEvent.mockWith(model: originalErrorEvent))?.model - let mappedResourceEvent = mapper.map(event: RUMEvent.mockWith(model: originalResourceEvent))?.model - let mappedActionEvent = mapper.map(event: RUMEvent.mockWith(model: originalActionEvent))?.model + let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model + let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model + let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model // Then XCTAssertNil(mappedErrorEvent, "Mapper should return nil.") @@ -135,10 +102,10 @@ class RUMEventsMapperTests: XCTestCase { ) // When - let mappedViewEvent = mapper.map(event: RUMEvent.mockWith(model: originalViewEvent))?.model - let mappedErrorEvent = mapper.map(event: RUMEvent.mockWith(model: originalErrorEvent))?.model - let mappedResourceEvent = mapper.map(event: RUMEvent.mockWith(model: originalResourceEvent))?.model - let mappedActionEvent = mapper.map(event: RUMEvent.mockWith(model: originalActionEvent))?.model + let mappedViewEvent = mapper.map(event: RUMEvent(model: originalViewEvent))?.model + let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model + let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model + let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model // Then XCTAssertEqual(try XCTUnwrap(mappedViewEvent), originalViewEvent, "Mapper should return the original event.") diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index eec87c6e70..dc032d3627 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -614,7 +614,11 @@ class RUMMonitorTests: XCTestCase { monitor.stopView(viewController: mockView) let rumEventMatchers = try RUMFeature.waitAndReturnRUMEventMatchers(count: 11) - let expectedUserInfo = RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: [:]) + let expectedUserInfo = RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: [ + "str": CodableValue("value"), + "int": CodableValue(11_235), + "bool": CodableValue(true) + ]) try rumEventMatchers.forEach { event in XCTAssertEqual(try event.attribute(forKeyPath: "usr.str"), "value") XCTAssertEqual(try event.attribute(forKeyPath: "usr.int"), 11_235) @@ -998,8 +1002,8 @@ class RUMMonitorTests: XCTestCase { // MARK: - Integration with Crash Reporting func testGivenRegisteredCrashReporter_whenRUMViewEventIsSend_itIsUpdatedInCurrentCrashContext() throws { - let randomUserInfoAttributes: [String: String] = .mockRandom() - let randomViewEventAttributes: [String: String] = .mockRandom() + let randomUserInfoAttributes = mockRandomAttributes() + let randomViewEventAttributes = mockRandomAttributes() RUMFeature.instance = .mockByRecordingRUMEventMatchers( directories: temporaryFeatureDirectories, @@ -1032,18 +1036,9 @@ class RUMMonitorTests: XCTestCase { let lastRUMViewEventSent: RUMViewEvent = try rumEventMatchers[1].model() let currentCrashContext = try XCTUnwrap(Global.crashReporter?.crashContextProvider.currentCrashContext) - XCTAssertEqual( - currentCrashContext.lastRUMViewEvent?.model, - lastRUMViewEventSent - ) - XCTAssertEqual( - currentCrashContext.lastRUMViewEvent?.attributes as? [String: String], - randomViewEventAttributes - ) - XCTAssertEqual( - currentCrashContext.lastRUMViewEvent?.userInfoAttributes as? [String: String], - randomUserInfoAttributes - ) + let currentLastRUMViewEventSent = try XCTUnwrap(currentCrashContext.lastRUMViewEvent?.model) + + try AssertEncodedRepresentationsEqual(value1: currentLastRUMViewEventSent, value2: lastRUMViewEventSent) } // MARK: - Thread safety From 183099c215b82eabb939c84cb420ef61c0127ab8 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 09:09:27 +0200 Subject: [PATCH 19/80] RUMM-1420 Remove redundant TODO --- Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift index 05c3454a40..7ca3f6472f 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift @@ -8,13 +8,13 @@ import Foundation /// `Encodable` representation of RUM event. /// Mutable properties are subject of sanitization or data scrubbing. -/// /// TODO: RUMM-1463 Remove `errorAttributes` property once new error format is managed through `RUMDataModels` /// TODO: RUMM-1584 - Remove `RUMEvent` container. internal struct RUMEvent: Encodable where DM: RUMDataModel, DM: RUMSanitizableEvent { /// The actual RUM event model created by `RUMMonitor` var model: DM - /// Error attributes. + /// Error attributes. Only set when `DM == RUMErrorEvent` and error describes a crash. + /// Can be entirely removed when RUMM-1463 is resolved and error values are part of the `RUMErrorEvent`. let errorAttributes: [String: Encodable]? /// Creates a RUM Event object object based on the given sanitizable model. From fb0363dd0cb36c2a1911bec74341f5a68baadd78 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 09:12:54 +0200 Subject: [PATCH 20/80] RUMM-1420 Apply RandomMock to RUMEvent --- .../Datadog/Core/Utils/ValuePublisher.swift | 2 +- .../CrashContextProviderTests.swift | 16 +++++------ ...ReportingWithLoggingIntegrationTests.swift | 2 +- ...rashReportingWithRUMIntegrationTests.swift | 8 +++--- .../RUMWithCrashContextIntegrationTests.swift | 2 +- .../Datadog/Mocks/CoreMocks.swift | 10 +++++-- .../Mocks/CrashReportingFeatureMocks.swift | 2 +- .../Datadog/Mocks/RUMDataModelMocks.swift | 10 +++---- .../Datadog/Mocks/RUMFeatureMocks.swift | 27 +++++-------------- .../SystemFrameworks/FoundationMocks.swift | 6 +++++ 10 files changed, 41 insertions(+), 44 deletions(-) diff --git a/Sources/Datadog/Core/Utils/ValuePublisher.swift b/Sources/Datadog/Core/Utils/ValuePublisher.swift index 8e9f97a0ca..469a50d810 100644 --- a/Sources/Datadog/Core/Utils/ValuePublisher.swift +++ b/Sources/Datadog/Core/Utils/ValuePublisher.swift @@ -48,7 +48,7 @@ internal class ValuePublisher { } } - init(initialValue: Value) { + required init(initialValue: Value) { self.unsafeValue = initialValue self.unsafeObservers = [] } diff --git a/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift b/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift index 96cf8d9ac8..0cd7fef4c7 100644 --- a/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift +++ b/Tests/DatadogTests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift @@ -23,7 +23,7 @@ class CrashContextProviderTests: XCTestCase { userInfoProvider: .mockAny(), networkConnectionInfoProvider: NetworkConnectionInfoProviderMock.mockAny(), carrierInfoProvider: CarrierInfoProviderMock.mockAny(), - rumViewEventProvider: .mockAny() + rumViewEventProvider: .mockRandom() ) let initialContext = crashContextProvider.currentCrashContext @@ -46,7 +46,7 @@ class CrashContextProviderTests: XCTestCase { func testWhenRUMWithCrashContextIntegrationIsUpdated_thenCrashContextProviderNotifiesNewContext() { let expectation = self.expectation(description: "Notify new crash context") - let randomRUMViewEvent: RUMEvent = .mockRandomWith(model: RUMViewEvent.mockRandom()) + let randomRUMViewEvent: RUMEvent = RUMEvent(model: RUMViewEvent.mockRandom()) let rumViewEventProvider = ValuePublisher?>(initialValue: randomRUMViewEvent) let crashContextProvider = CrashContextProvider( @@ -89,7 +89,7 @@ class CrashContextProviderTests: XCTestCase { userInfoProvider: userInfoProvider, networkConnectionInfoProvider: NetworkConnectionInfoProviderMock.mockAny(), carrierInfoProvider: CarrierInfoProviderMock.mockAny(), - rumViewEventProvider: .mockAny() + rumViewEventProvider: .mockRandom() ) let initialContext = crashContextProvider.currentCrashContext @@ -121,7 +121,7 @@ class CrashContextProviderTests: XCTestCase { userInfoProvider: .mockAny(), networkConnectionInfoProvider: mainProvider, carrierInfoProvider: CarrierInfoProviderMock.mockAny(), - rumViewEventProvider: .mockAny() + rumViewEventProvider: .mockRandom() ) let initialContext = crashContextProvider.currentCrashContext @@ -154,7 +154,7 @@ class CrashContextProviderTests: XCTestCase { userInfoProvider: .mockAny(), networkConnectionInfoProvider: NetworkConnectionInfoProviderMock.mockAny(), carrierInfoProvider: mainProvider, - rumViewEventProvider: .mockAny() + rumViewEventProvider: .mockRandom() ) let initialContext = crashContextProvider.currentCrashContext @@ -178,7 +178,7 @@ class CrashContextProviderTests: XCTestCase { func testWhenContextIsWrittenAndReadFromDifferentThreads_itRunsAllOperationsSafely() { let consentProvider: ConsentProvider = .mockAny() - let rumViewEventProvider: ValuePublisher?> = .mockAny() + let rumViewEventProvider: ValuePublisher?> = .mockRandom() let userInfoProvider: UserInfoProvider = .mockAny() let networkInfoWrappedProvider = NetworkConnectionInfoProviderMock(networkConnectionInfo: .mockRandom()) let networkInfoMainProvider = NetworkConnectionInfoProvider(wrappedProvider: networkInfoWrappedProvider) @@ -190,7 +190,7 @@ class CrashContextProviderTests: XCTestCase { userInfoProvider: userInfoProvider, networkConnectionInfoProvider: networkInfoMainProvider, carrierInfoProvider: carrierInfoMainProvider, - rumViewEventProvider: .mockAny() + rumViewEventProvider: .mockRandom() ) withExtendedLifetime(provider) { @@ -208,7 +208,7 @@ class CrashContextProviderTests: XCTestCase { carrierInfoWrappedProvider.set(current: .mockRandom()) _ = carrierInfoMainProvider.current }, - { rumViewEventProvider.publishSyncOrAsync(.mockRandomWith(model: RUMViewEvent.mockRandom())) }, + { rumViewEventProvider.publishSyncOrAsync(.mockRandom()) }, ], iterations: 50 ) diff --git a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithLoggingIntegrationTests.swift b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithLoggingIntegrationTests.swift index f14be49ceb..404a7aec0d 100644 --- a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithLoggingIntegrationTests.swift +++ b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithLoggingIntegrationTests.swift @@ -71,7 +71,7 @@ class CrashReportingWithLoggingIntegrationTests: XCTestCase { ) let crashContext: CrashContext = .mockWith( lastUserInfo: Bool.random() ? .mockRandom() : .empty, - lastRUMViewEvent: .mockRandomWith(model: RUMViewEvent.mockRandom()), + lastRUMViewEvent: .mockRandom(), lastNetworkConnectionInfo: .mockRandom(), lastCarrierInfo: .mockRandom() ) diff --git a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift index 80c3472dc4..17b135f0c2 100644 --- a/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift +++ b/Tests/DatadogTests/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegrationTests.swift @@ -22,7 +22,7 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { let crashReport: DDCrashReport = .mockWith(date: crashDate) let crashContext: CrashContext = .mockWith( lastTrackingConsent: .granted, - lastRUMViewEvent: .mockRandomWith(model: RUMViewEvent.mockRandom()) + lastRUMViewEvent: .mockRandom() ) // When @@ -49,7 +49,7 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { let crashReport: DDCrashReport = .mockWith(date: crashDate) let crashContext: CrashContext = .mockWith( lastTrackingConsent: .granted, - lastRUMViewEvent: .mockRandomWith(model: RUMViewEvent.mockRandom()) + lastRUMViewEvent: .mockRandom() ) // When @@ -70,7 +70,7 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { let crashReport: DDCrashReport = .mockWith(date: .mockDecember15th2019At10AMUTC()) let crashContext: CrashContext = .mockWith( lastTrackingConsent: [.pending, .notGranted].randomElement()!, - lastRUMViewEvent: .mockRandomWith(model: RUMViewEvent.mockRandom()) + lastRUMViewEvent: .mockRandom() ) // When @@ -198,7 +198,7 @@ class CrashReportingWithRUMIntegrationTests: XCTestCase { ) let crashContext: CrashContext = .mockWith( lastTrackingConsent: .granted, - lastRUMViewEvent: .mockRandomWith(model: lastRUMViewEvent) + lastRUMViewEvent: RUMEvent(model: lastRUMViewEvent) ) // When diff --git a/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMWithCrashContextIntegrationTests.swift b/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMWithCrashContextIntegrationTests.swift index 48b473631f..f177f9b320 100644 --- a/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMWithCrashContextIntegrationTests.swift +++ b/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMWithCrashContextIntegrationTests.swift @@ -15,7 +15,7 @@ class RUMWithCrashContextIntegrationTests: XCTestCase { // Then let rumWithCrashContextIntegration = try XCTUnwrap(RUMWithCrashContextIntegration()) - let randomRUMViewEvent: RUMEvent = .mockRandomWith(model: RUMViewEvent.mockRandom()) + let randomRUMViewEvent: RUMEvent = .mockRandom() rumWithCrashContextIntegration.update(lastRUMViewEvent: randomRUMViewEvent) XCTAssertEqual(CrashReportingFeature.instance?.rumViewEventProvider.currentValue, randomRUMViewEvent) diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index 3fbce215db..085b28e64a 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -957,12 +957,18 @@ extension CodableValue { } } -extension ValuePublisher where Value: AnyMockable { - static func mockAny() -> ValuePublisher { +extension ValuePublisher: AnyMockable where Value: AnyMockable { + static func mockAny() -> Self { return .init(initialValue: .mockAny()) } } +extension ValuePublisher: RandomMockable where Value: RandomMockable { + static func mockRandom() -> Self { + return .init(initialValue: .mockRandom()) + } +} + extension ValuePublisher { /// Publishes `newValue` using `publishSync(:_)` or `publishAsync(:_)`. func publishSyncOrAsync(_ newValue: Value) { diff --git a/Tests/DatadogTests/Datadog/Mocks/CrashReportingFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CrashReportingFeatureMocks.swift index bf0b5f3e5f..c2a17a1339 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CrashReportingFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CrashReportingFeatureMocks.swift @@ -105,7 +105,7 @@ extension CrashContext { return CrashContext( lastTrackingConsent: .mockRandom(), lastUserInfo: .mockRandom(), - lastRUMViewEvent: .mockRandomWith(model: RUMViewEvent.mockRandom()), + lastRUMViewEvent: .mockRandom(), lastNetworkConnectionInfo: .mockRandom(), lastCarrierInfo: .mockRandom() ) diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift index 6b2a92b614..4249c7293c 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift @@ -38,7 +38,7 @@ extension RUMConnectivity { } } -extension RUMMethod { +extension RUMMethod: RandomMockable { static func mockRandom() -> RUMMethod { return [.post, .get, .head, .put, .delete, .patch].randomElement()! } @@ -50,7 +50,7 @@ extension RUMEventAttributes: RandomMockable { } } -extension RUMViewEvent { +extension RUMViewEvent: RandomMockable { static func mockRandom() -> RUMViewEvent { return RUMViewEvent( dd: .init( @@ -109,7 +109,7 @@ extension RUMViewEvent { } } -extension RUMResourceEvent { +extension RUMResourceEvent: RandomMockable { static func mockRandom() -> RUMResourceEvent { return RUMResourceEvent( dd: .init( @@ -158,7 +158,7 @@ extension RUMResourceEvent { } } -extension RUMActionEvent { +extension RUMActionEvent: RandomMockable { static func mockRandom() -> RUMActionEvent { return RUMActionEvent( dd: .init( @@ -195,7 +195,7 @@ extension RUMActionEvent { } } -extension RUMErrorEvent { +extension RUMErrorEvent: RandomMockable { static func mockRandom() -> RUMErrorEvent { return RUMErrorEvent( dd: .init( diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index 8025649856..edda18e528 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -96,30 +96,15 @@ struct RUMDataModelMock: RUMDataModel, RUMSanitizableEvent, EquatableInTests { // MARK: - Component Mocks -extension RUMEvent: AnyMockable where DM == RUMViewEvent { - static func mockAny() -> RUMEvent { - return RUMEvent(model: RUMViewEvent.mockRandom()) +extension RUMEvent: AnyMockable where DM: AnyMockable { + static func mockAny() -> RUMEvent { + return RUMEvent(model: .mockAny()) } } -extension RUMEvent { - static func mockRandomWith(model: DM) -> RUMEvent { - func randomAttributes(prefixed prefix: String) -> [String: Encodable] { - var attributes: [String: String] = [:] - (0..<10).forEach { index in attributes["\(prefix)\(index)"] = "value\(index)" } - return attributes - } - - var model = model - model.context = RUMEventAttributes(contextInfo: randomAttributes(prefixed: "event-attribute")) - model.usr = RUMUser( - email: model.usr?.email, - id: model.usr?.id, - name: model.usr?.name, - usrInfo: randomAttributes(prefixed: "user-attribute") - ) - - return RUMEvent(model: model) +extension RUMEvent: RandomMockable where DM: RandomMockable { + static func mockRandom() -> RUMEvent { + return RUMEvent(model: .mockRandom()) } } diff --git a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift index 0e6939d1fe..e31592a06a 100644 --- a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift @@ -65,6 +65,12 @@ extension Optional: AnyMockable where Wrapped: AnyMockable { } } +extension Optional: RandomMockable where Wrapped: RandomMockable { + static func mockRandom() -> Self { + return .some(.mockRandom()) + } +} + extension Array where Element == Data { /// Returns chunks of mocked data. Accumulative size of all chunks equals `totalSize`. static func mockChunksOf(totalSize: UInt64, maxChunkSize: UInt64) -> [Data] { From cb8010c7c0c1b9e138a8951ff251bb094826572a Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 09:13:47 +0200 Subject: [PATCH 21/80] RUMM-1420 Assert RUMEvent user info --- .../Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift index 09ed959bae..7dcfd047c1 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift @@ -9,7 +9,13 @@ import XCTest class RUMEventBuilderTests: XCTestCase { func testItBuildsRUMEvent() throws { - let builder = RUMEventBuilder(userInfoProvider: .mockAny(), eventsMapper: .mockNoOp()) + let builder = RUMEventBuilder( + userInfoProvider: .mockWith( + userInfo: .init(id: nil, name: nil, email: nil, extraInfo: ["bazz": "buzz"]) + ), + eventsMapper: .mockNoOp() + ) + let event = try XCTUnwrap( builder.createRUMEvent( with: RUMDataModelMock(attribute: "foo"), @@ -20,6 +26,7 @@ class RUMEventBuilderTests: XCTestCase { XCTAssertEqual(event.model.attribute, "foo") XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["foo"], "bar") XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["fizz"], "buzz") + XCTAssertEqual((event.model.usr?.usrInfo as? [String: String])?["bazz"], "buzz") } func testGivenEventBuilderWithEventMapper_whenEventIsModified_itBuildsModifiedEvent() throws { From 67de14ee60b49616bdcb43efa05db2f42ebe5df2 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 10:23:31 +0200 Subject: [PATCH 22/80] RUMM-1420 Remove `userInfoProvider` from `RUMEvemtBuilder` --- .../RUM/RUMEvent/RUMEventBuilder.swift | 11 +---------- .../RUM/RUMEvent/RUMUserInfoProvider.swift | 19 ++++++++++--------- Sources/Datadog/RUMMonitor.swift | 1 - .../Datadog/Mocks/RUMFeatureMocks.swift | 4 ++-- .../RUM/RUMEvent/RUMEventBuilderTests.swift | 11 +---------- .../RUMEvent/RUMUserInfoProviderTests.swift | 5 +++-- .../RUMEventFileOutputTests.swift | 2 +- .../Scopes/RUMResourceScopeTests.swift | 1 - .../Scopes/RUMUserActionScopeTests.swift | 3 +-- .../RUMMonitor/Scopes/RUMViewScopeTests.swift | 6 ++---- 10 files changed, 21 insertions(+), 42 deletions(-) diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift index 366d75640b..7c8638066c 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift @@ -7,14 +7,9 @@ import Foundation internal class RUMEventBuilder { - let userInfoProvider: UserInfoProvider let eventsMapper: RUMEventsMapper - init( - userInfoProvider: UserInfoProvider, - eventsMapper: RUMEventsMapper - ) { - self.userInfoProvider = userInfoProvider + init(eventsMapper: RUMEventsMapper) { self.eventsMapper = eventsMapper } @@ -28,10 +23,6 @@ internal class RUMEventBuilder { model.context = RUMEventAttributes(contextInfo: attributes) } - if !userInfoProvider.value.extraInfo.isEmpty { - model.usr = RUMUser(email: model.usr?.email, id: model.usr?.id, name: model.usr?.name, usrInfo: userInfoProvider.value.extraInfo) - } - let event = RUMEvent(model: model) let mappedEvent = eventsMapper.map(event: event) return mappedEvent diff --git a/Sources/Datadog/RUM/RUMEvent/RUMUserInfoProvider.swift b/Sources/Datadog/RUM/RUMEvent/RUMUserInfoProvider.swift index 5925431c0e..7905bcbcd7 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMUserInfoProvider.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMUserInfoProvider.swift @@ -12,17 +12,18 @@ internal struct RUMUserInfoProvider { let userInfoProvider: UserInfoProvider var current: RUMUser? { - let userInfo = userInfoProvider.value + let user = userInfoProvider.value - if userInfo.id == nil && userInfo.name == nil && userInfo.email == nil { + // Returns nil if UserInfo has no data + if user.id == nil, user.name == nil, user.email == nil, user.extraInfo.isEmpty { return nil - } else { - return RUMUser( - email: userInfo.email, - id: userInfo.id, - name: userInfo.name, - usrInfo: [:] - ) } + + return RUMUser( + email: user.email, + id: user.id, + name: user.name, + usrInfo: user.extraInfo + ) } } diff --git a/Sources/Datadog/RUMMonitor.swift b/Sources/Datadog/RUMMonitor.swift index 01f2c4ac54..9f12da6eca 100644 --- a/Sources/Datadog/RUMMonitor.swift +++ b/Sources/Datadog/RUMMonitor.swift @@ -180,7 +180,6 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber { carrierInfoProvider: rumFeature.carrierInfoProvider ), eventBuilder: RUMEventBuilder( - userInfoProvider: rumFeature.userInfoProvider, eventsMapper: rumFeature.eventsMapper ), eventOutput: RUMEventFileOutput( diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index edda18e528..cb6667481f 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -110,7 +110,7 @@ extension RUMEvent: RandomMockable where DM: RandomMockable { extension RUMEventBuilder { static func mockAny() -> RUMEventBuilder { - return RUMEventBuilder(userInfoProvider: UserInfoProvider.mockAny(), eventsMapper: RUMEventsMapper.mockNoOp()) + return RUMEventBuilder(eventsMapper: .mockNoOp()) } } @@ -393,7 +393,7 @@ extension RUMScopeDependencies { networkConnectionInfoProvider: NetworkConnectionInfoProviderMock(networkConnectionInfo: nil), carrierInfoProvider: CarrierInfoProviderMock(carrierInfo: nil) ), - eventBuilder: RUMEventBuilder = RUMEventBuilder(userInfoProvider: UserInfoProvider.mockAny(), eventsMapper: RUMEventsMapper.mockNoOp()), + eventBuilder: RUMEventBuilder = RUMEventBuilder(eventsMapper: .mockNoOp()), eventOutput: RUMEventOutput = RUMEventOutputMock(), rumUUIDGenerator: RUMUUIDGenerator = DefaultRUMUUIDGenerator(), dateCorrector: DateCorrectorType = DateCorrectorMock() diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift index 7dcfd047c1..d1cc54358a 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift @@ -9,12 +9,7 @@ import XCTest class RUMEventBuilderTests: XCTestCase { func testItBuildsRUMEvent() throws { - let builder = RUMEventBuilder( - userInfoProvider: .mockWith( - userInfo: .init(id: nil, name: nil, email: nil, extraInfo: ["bazz": "buzz"]) - ), - eventsMapper: .mockNoOp() - ) + let builder = RUMEventBuilder(eventsMapper: .mockNoOp()) let event = try XCTUnwrap( builder.createRUMEvent( @@ -26,12 +21,10 @@ class RUMEventBuilderTests: XCTestCase { XCTAssertEqual(event.model.attribute, "foo") XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["foo"], "bar") XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["fizz"], "buzz") - XCTAssertEqual((event.model.usr?.usrInfo as? [String: String])?["bazz"], "buzz") } func testGivenEventBuilderWithEventMapper_whenEventIsModified_itBuildsModifiedEvent() throws { let builder = RUMEventBuilder( - userInfoProvider: .mockAny(), eventsMapper: .mockWith( viewEventMapper: { viewEvent in return RUMViewEvent.mockRandom() @@ -50,7 +43,6 @@ class RUMEventBuilderTests: XCTestCase { func testGivenEventBuilderWithEventMapper_whenEventIsDropped_itBuildsNoEvent() { let builder = RUMEventBuilder( - userInfoProvider: .mockAny(), eventsMapper: .mockWith( resourceEventMapper: { event in return nil @@ -66,7 +58,6 @@ class RUMEventBuilderTests: XCTestCase { func testGivenEventBuilderWithNoEventMapper_whenBuildingAnEvent_itBuildsEventWithOriginalModel() throws { let builder = RUMEventBuilder( - userInfoProvider: .mockAny(), eventsMapper: .mockWith( resourceEventMapper: { event in return event diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift index 4b9637b8e7..4041b16f2b 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift @@ -29,8 +29,9 @@ class RUMUserInfoProviderTests: XCTestCase { userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: [:]) XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: [:])) - userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: mockRandomAttributes()) + let extraInfo: [String: Encodable] = mockRandomAttributes() + userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: extraInfo) // TODO: RUMM-1420 Encode user `extraInfo` info as RUMUser `usrInfo` - XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: [:])) + XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: extraInfo)) } } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift index 710b1b952e..654314294a 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift @@ -20,7 +20,7 @@ class RUMEventFileOutputTests: XCTestCase { func testItWritesRUMEventToFileAsJSON() throws { let fileCreationDateProvider = RelativeDateProvider(startingFrom: .mockDecember15th2019At10AMUTC()) - let builder = RUMEventBuilder(userInfoProvider: .mockAny(), eventsMapper: .mockNoOp()) + let builder = RUMEventBuilder(eventsMapper: .mockNoOp()) let output = RUMEventFileOutput( fileWriter: FileWriter( dataFormat: RUMFeature.dataFormat, diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift index 5874098d75..88ae95f9fd 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift @@ -593,7 +593,6 @@ class RUMResourceScopeTests: XCTestCase { // Given let eventBuilder = RUMEventBuilder( - userInfoProvider: UserInfoProvider.mockAny(), eventsMapper: RUMEventsMapper.mockWith( errorEventMapper: { event in nil diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift index 09b5dd3f86..2fce50e60c 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift @@ -396,8 +396,7 @@ class RUMUserActionScopeTests: XCTestCase { func testGivenUserActionScopeWithEventSentCallback_whenBypassingSendingEvent_thenCallbackIsNotCalled() { // swiftlint:disable trailing_closure let eventBuilder = RUMEventBuilder( - userInfoProvider: UserInfoProvider.mockAny(), - eventsMapper: RUMEventsMapper.mockWith( + eventsMapper: .mockWith( actionEventMapper: { event in nil } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index b87e538961..ee6ee1b781 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -848,8 +848,7 @@ class RUMViewScopeTests: XCTestCase { // - discards `RUMErrorEvent` for `RUMAddCurrentViewErrorCommand` // - discards `RUMResourceEvent` from `RUMStartResourceCommand` /resource/1 let eventBuilder = RUMEventBuilder( - userInfoProvider: UserInfoProvider.mockAny(), - eventsMapper: RUMEventsMapper.mockWith( + eventsMapper: .mockWith( errorEventMapper: { event in nil }, @@ -935,8 +934,7 @@ class RUMViewScopeTests: XCTestCase { func testGivenViewScopeWithDroppingEventsMapper_whenProcessingApplicationStartAction_thenNoEventIsSent() throws { let eventBuilder = RUMEventBuilder( - userInfoProvider: UserInfoProvider.mockAny(), - eventsMapper: RUMEventsMapper.mockWith( + eventsMapper: .mockWith( actionEventMapper: { event in nil } From fe202223b432b5bccb8cb5dc96afa793ca566edf Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 10:23:53 +0200 Subject: [PATCH 23/80] RUMM-1420 Fix assertion description --- .../Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift index 9d27117dab..9c7dde8080 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift @@ -113,8 +113,8 @@ class RUMEventSanitizerTests: XCTestCase { remaining -= expectedSanitizedAttrs XCTAssertGreaterThanOrEqual(remaining, 0) - XCTAssertEqual(sanitized.usr?.usrInfo.count, expectedSanitizedUserInfo, "If number of attributes needs to be limited, `userInfoAttributes` are removed second") - XCTAssertEqual(sanitized.context?.contextInfo.count, expectedSanitizedAttrs, "If number of attributes needs to be limited, `attributes` are removed first.") + XCTAssertEqual(sanitized.usr?.usrInfo.count, expectedSanitizedUserInfo, "If number of attributes needs to be limited, `usrInfo` are removed second") + XCTAssertEqual(sanitized.context?.contextInfo.count, expectedSanitizedAttrs, "If number of attributes needs to be limited, `contextInfo` are removed first.") } test(event: viewEvent) From 6413c6513dbd2a250d1793a8aad952e1fa299432 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 10:35:59 +0200 Subject: [PATCH 24/80] RUMM-1420 Fix linting --- .../Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift index 4041b16f2b..f3602ceda4 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift @@ -29,7 +29,7 @@ class RUMUserInfoProviderTests: XCTestCase { userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: [:]) XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: [:])) - let extraInfo: [String: Encodable] = mockRandomAttributes() + let extraInfo: [String: Encodable] = mockRandomAttributes() userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: extraInfo) // TODO: RUMM-1420 Encode user `extraInfo` info as RUMUser `usrInfo` XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: extraInfo)) From b699ac344abcfdf6d78f93d489f478136b575648 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 11:01:38 +0200 Subject: [PATCH 25/80] RUMM-1420 Fix benchmark test build --- .../DataStorage/RUMStorageBenchmarkTests.swift | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Tests/DatadogBenchmarkTests/DataStorage/RUMStorageBenchmarkTests.swift b/Tests/DatadogBenchmarkTests/DataStorage/RUMStorageBenchmarkTests.swift index f1e0952b81..6ff278e9c7 100644 --- a/Tests/DatadogBenchmarkTests/DataStorage/RUMStorageBenchmarkTests.swift +++ b/Tests/DatadogBenchmarkTests/DataStorage/RUMStorageBenchmarkTests.swift @@ -43,7 +43,7 @@ class RUMStorageBenchmarkTests: XCTestCase { } func testWrittingRUMEventsOnDisc() throws { - let event = createRandomizedRUMEvent() + let event = RUMEvent(model: RUMViewEvent.mockRandom()) measure { writer.write(value: event) @@ -53,7 +53,7 @@ class RUMStorageBenchmarkTests: XCTestCase { func testReadingRUMEventsFromDisc() throws { while try directory.files().count < 10 { // `measureMetrics {}` is fired 10 times so 10 batch files are required - writer.write(value: createRandomizedRUMEvent()) + writer.write(value: RUMEvent(model: RUMViewEvent.mockRandom())) queue.sync {} // wait to complete async write } @@ -72,14 +72,4 @@ class RUMStorageBenchmarkTests: XCTestCase { } } } - - // MARK: - Helpers - - private func createRandomizedRUMEvent() -> RUMEvent { - return RUMEvent( - model: .mockRandom(), - attributes: mockRandomAttributes(), - userInfoAttributes: mockRandomAttributes() - ) - } } From 3ab4322f14c739e6e1155034da2c780be0808271 Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Wed, 1 Sep 2021 12:33:04 +0200 Subject: [PATCH 26/80] RUMM-1350 let customer set up a Proxy for data upload --- .../Datadog/Core/FeaturesConfiguration.swift | 4 +++- Sources/Datadog/Core/Upload/HTTPClient.swift | 6 +++-- Sources/Datadog/Datadog.swift | 2 +- Sources/Datadog/DatadogConfiguration.swift | 10 ++++++++ .../Core/FeaturesConfigurationTests.swift | 23 +++++++++++++++++-- .../Datadog/Core/Upload/HTTPClientTests.swift | 19 +++++++++++++++ .../DatadogConfigurationBuilderTests.swift | 12 ++++++++++ .../Datadog/Mocks/CoreMocks.swift | 8 +++++-- 8 files changed, 76 insertions(+), 8 deletions(-) diff --git a/Sources/Datadog/Core/FeaturesConfiguration.swift b/Sources/Datadog/Core/FeaturesConfiguration.swift index 4b491eca26..0e9ac177cc 100644 --- a/Sources/Datadog/Core/FeaturesConfiguration.swift +++ b/Sources/Datadog/Core/FeaturesConfiguration.swift @@ -19,6 +19,7 @@ internal struct FeaturesConfiguration { let environment: String let performance: PerformancePreset let source: String + let proxyConfiguration: [AnyHashable: Any]? } struct Logging { @@ -156,7 +157,8 @@ extension FeaturesConfiguration { uploadFrequency: configuration.uploadFrequency, bundleType: appContext.bundleType ), - source: source + source: source, + proxyConfiguration: configuration.proxyConfiguration ) if configuration.loggingEnabled { diff --git a/Sources/Datadog/Core/Upload/HTTPClient.swift b/Sources/Datadog/Core/Upload/HTTPClient.swift index 6851084d27..8a028ffa4f 100644 --- a/Sources/Datadog/Core/Upload/HTTPClient.swift +++ b/Sources/Datadog/Core/Upload/HTTPClient.swift @@ -8,14 +8,16 @@ import Foundation /// Client for sending requests over HTTP. internal class HTTPClient { - private let session: URLSession + internal let session: URLSession - convenience init() { + convenience init(proxyConfiguration: [AnyHashable: Any]? = nil) { let configuration: URLSessionConfiguration = .ephemeral // NOTE: RUMM-610 Default behaviour of `.ephemeral` session is to cache requests. // To not leak requests memory (including their `.httpBody` which may be significant) // we explicitly opt-out from using cache. This cannot be achieved using `.requestCachePolicy`. configuration.urlCache = nil + configuration.connectionProxyDictionary = proxyConfiguration + // TODO: RUMM-123 Optimize `URLSessionConfiguration` for good traffic performance // and move session configuration constants to `PerformancePreset`. self.init(session: URLSession(configuration: configuration)) diff --git a/Sources/Datadog/Datadog.swift b/Sources/Datadog/Datadog.swift index 872daca099..c7a3ba8e5c 100644 --- a/Sources/Datadog/Datadog.swift +++ b/Sources/Datadog/Datadog.swift @@ -187,7 +187,7 @@ public class Datadog { let commonDependencies = FeaturesCommonDependencies( consentProvider: consentProvider, performance: configuration.common.performance, - httpClient: HTTPClient(), + httpClient: HTTPClient(proxyConfiguration: configuration.common.proxyConfiguration), mobileDevice: MobileDevice.current, dateProvider: dateProvider, dateCorrector: dateCorrector, diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index 5db1050128..1b8c954c8f 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -244,6 +244,7 @@ extension Datadog { private(set) var batchSize: BatchSize private(set) var uploadFrequency: UploadFrequency private(set) var additionalConfiguration: [String: Any] + private(set) var proxyConfiguration: [AnyHashable: Any]? /// The client token autorizing internal monitoring data to be sent to Datadog org. private(set) var internalMonitoringClientToken: String? @@ -316,6 +317,7 @@ extension Datadog { batchSize: .medium, uploadFrequency: .average, additionalConfiguration: [:], + proxyConfiguration: nil, internalMonitoringClientToken: nil ) } @@ -692,6 +694,14 @@ extension Datadog { return self } + /// Sets proxy configuration attributes. + /// This can be used to a enable a custom proxy for uploading tracked data to Datadog's intake. + /// - Parameter proxyConfiguration: `nil` by default. + public func set(proxyConfiguration: [AnyHashable: Any]?) -> Builder { + configuration.proxyConfiguration = proxyConfiguration + return self + } + /// Sets additional configuration attributes. /// This can be used to tweak internal features of the SDK. /// - Parameter additionalConfiguration: `[:]` by default. diff --git a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift index 03f965717a..afce36b1d4 100644 --- a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift +++ b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift @@ -251,6 +251,23 @@ class FeaturesConfigurationTests: XCTestCase { ) } + func testCustomProxy() throws { + let proxyConfiguration: [AnyHashable: Any] = [ + kCFNetworkProxiesHTTPEnable as AnyHashable: true, + kCFNetworkProxiesHTTPPort as AnyHashable: 123, + kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", + kCFProxyUsernameKey as AnyHashable: "proxyuser", + kCFProxyPasswordKey as AnyHashable: "proxypass", + ] + let configuration = try createConfiguration(proxyConfiguration: proxyConfiguration) + + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + } + // MARK: - Logging Configuration Tests func testWhenLoggingIsDisabled() throws { @@ -752,7 +769,8 @@ class FeaturesConfigurationTests: XCTestCase { customRUMEndpoint: URL? = nil, logsEndpoint: Datadog.Configuration.LogsEndpoint = .us1, tracesEndpoint: Datadog.Configuration.TracesEndpoint = .us1, - rumEndpoint: Datadog.Configuration.RUMEndpoint = .us1 + rumEndpoint: Datadog.Configuration.RUMEndpoint = .us1, + proxyConfiguration: [AnyHashable: Any]? = nil ) throws -> FeaturesConfiguration { return try FeaturesConfiguration( configuration: .mockWith( @@ -766,7 +784,8 @@ class FeaturesConfigurationTests: XCTestCase { customRUMEndpoint: customRUMEndpoint, logsEndpoint: logsEndpoint, tracesEndpoint: tracesEndpoint, - rumEndpoint: rumEndpoint + rumEndpoint: rumEndpoint, + proxyConfiguration: proxyConfiguration ), appContext: .mockAny() ) diff --git a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift index 1f2b1d0f40..57ae205cca 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift @@ -46,4 +46,23 @@ class HTTPClientTests: XCTestCase { waitForExpectations(timeout: 1, handler: nil) server.waitFor(requestsCompletion: 1) } + + func testWhenProxyConfigurationIsSet_itUsesProxyConfiguration() { + let proxyConfiguration: [AnyHashable: Any] = [ + kCFNetworkProxiesHTTPEnable as AnyHashable: true, + kCFNetworkProxiesHTTPPort as AnyHashable: 123, + kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", + kCFProxyUsernameKey as AnyHashable: "proxyuser", + kCFProxyPasswordKey as AnyHashable: "proxypass", + ] + + let client = HTTPClient(proxyConfiguration: proxyConfiguration) + + let actualProxy: [AnyHashable: Any] = client.session.configuration.connectionProxyDictionary! + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") + XCTAssertEqual(actualProxy[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") + XCTAssertEqual(actualProxy[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + } } diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index 40bd3b8eea..2bf1fa4f4f 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -91,6 +91,13 @@ class DatadogConfigurationBuilderTests: XCTestCase { .set(batchSize: .small) .set(uploadFrequency: .frequent) .set(additionalConfiguration: ["foo": 42, "bar": "something"]) + .set(proxyConfiguration: [ + kCFNetworkProxiesHTTPEnable as AnyHashable: true, + kCFNetworkProxiesHTTPPort as AnyHashable: 123, + kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", + kCFProxyUsernameKey as AnyHashable: "proxyuser", + kCFProxyPasswordKey as AnyHashable: "proxypass", + ]) return builder } @@ -139,6 +146,11 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertEqual(configuration.uploadFrequency, .frequent) XCTAssertEqual(configuration.additionalConfiguration["foo"] as? Int, 42) XCTAssertEqual(configuration.additionalConfiguration["bar"] as? String, "something") + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") + XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") + XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") } XCTAssertTrue(rumConfigurationWithDefaultValues.rumUIKitViewsPredicate is DefaultUIKitRUMViewsPredicate) diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index 3fbce215db..ed989fb31a 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -57,6 +57,7 @@ extension Datadog.Configuration { batchSize: BatchSize = .medium, uploadFrequency: UploadFrequency = .average, additionalConfiguration: [String: Any] = [:], + proxyConfiguration: [AnyHashable: Any]? = nil, internalMonitoringClientToken: String? = nil ) -> Datadog.Configuration { return Datadog.Configuration( @@ -84,6 +85,7 @@ extension Datadog.Configuration { batchSize: batchSize, uploadFrequency: uploadFrequency, additionalConfiguration: additionalConfiguration, + proxyConfiguration: proxyConfiguration, internalMonitoringClientToken: internalMonitoringClientToken ) } @@ -171,7 +173,8 @@ extension FeaturesConfiguration.Common { serviceName: String = .mockAny(), environment: String = .mockAny(), performance: PerformancePreset = .init(batchSize: .medium, uploadFrequency: .average, bundleType: .iOSApp), - source: String = .mockAny() + source: String = .mockAny(), + proxyConfiguration: [AnyHashable: Any]? = nil ) -> Self { return .init( applicationName: applicationName, @@ -180,7 +183,8 @@ extension FeaturesConfiguration.Common { serviceName: serviceName, environment: environment, performance: performance, - source: source + source: source, + proxyConfiguration: proxyConfiguration ) } } From e1f518d5285dfc8f4421ab82b024618cda34fbdd Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Wed, 1 Sep 2021 13:20:53 +0200 Subject: [PATCH 27/80] RUMM-1350 add proxy configuration in ObjC --- Sources/DatadogObjc/DatadogConfiguration+objc.swift | 5 +++++ Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index b5caad85dc..0cb0aea277 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -331,6 +331,11 @@ public class DDConfigurationBuilder: NSObject { _ = sdkBuilder.set(additionalConfiguration: additionalConfiguration) } + @objc + public func set(proxyConfiguration: [AnyHashable: Any]) { + _ = sdkBuilder.set(proxyConfiguration: proxyConfiguration) + } + @objc public func build() -> DDConfiguration { return DDConfiguration(sdkConfiguration: sdkBuilder.build()) diff --git a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift index 921cdf7c6c..8f235981a1 100644 --- a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift +++ b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift @@ -180,6 +180,13 @@ class DDConfigurationTests: XCTestCase { objcBuilder.set(additionalConfiguration: ["foo": 42, "bar": "something"]) XCTAssertEqual(objcBuilder.build().sdkConfiguration.additionalConfiguration["foo"] as? Int, 42) XCTAssertEqual(objcBuilder.build().sdkConfiguration.additionalConfiguration["bar"] as? String, "something") + + objcBuilder.set(proxyConfiguration: [kCFNetworkProxiesHTTPEnable as AnyHashable: true, kCFNetworkProxiesHTTPPort as AnyHashable: 123, kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", kCFProxyUsernameKey as AnyHashable: "proxyuser", kCFProxyPasswordKey as AnyHashable: "proxypass" ]) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") } func testScrubbingRUMEvents() { From 2b30de1cfeac45e3f4b98da066f2580c58c61952 Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Fri, 3 Sep 2021 08:46:49 +0200 Subject: [PATCH 28/80] RUMM-1350 remove unnecessary casts --- .../Core/FeaturesConfigurationTests.swift | 20 +++++++++---------- .../Datadog/Core/Upload/HTTPClientTests.swift | 20 +++++++++---------- .../DatadogConfigurationBuilderTests.swift | 20 +++++++++---------- .../DatadogObjc/DDConfigurationTests.swift | 12 +++++------ 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift index afce36b1d4..b65675746a 100644 --- a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift +++ b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift @@ -253,19 +253,19 @@ class FeaturesConfigurationTests: XCTestCase { func testCustomProxy() throws { let proxyConfiguration: [AnyHashable: Any] = [ - kCFNetworkProxiesHTTPEnable as AnyHashable: true, - kCFNetworkProxiesHTTPPort as AnyHashable: 123, - kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", - kCFProxyUsernameKey as AnyHashable: "proxyuser", - kCFProxyPasswordKey as AnyHashable: "proxypass", + kCFNetworkProxiesHTTPEnable: true, + kCFNetworkProxiesHTTPPort: 123, + kCFNetworkProxiesHTTPProxy: "www.example.com", + kCFProxyUsernameKey: "proxyuser", + kCFProxyPasswordKey: "proxypass", ] let configuration = try createConfiguration(proxyConfiguration: proxyConfiguration) - XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) - XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) - XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") - XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") - XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPEnable] as? Bool, true) + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPPort] as? Int, 123) + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyUsernameKey] as? String, "proxyuser") + XCTAssertEqual(configuration.common.proxyConfiguration?[kCFProxyPasswordKey] as? String, "proxypass") } // MARK: - Logging Configuration Tests diff --git a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift index 57ae205cca..e011afa88e 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift @@ -49,20 +49,20 @@ class HTTPClientTests: XCTestCase { func testWhenProxyConfigurationIsSet_itUsesProxyConfiguration() { let proxyConfiguration: [AnyHashable: Any] = [ - kCFNetworkProxiesHTTPEnable as AnyHashable: true, - kCFNetworkProxiesHTTPPort as AnyHashable: 123, - kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", - kCFProxyUsernameKey as AnyHashable: "proxyuser", - kCFProxyPasswordKey as AnyHashable: "proxypass", + kCFNetworkProxiesHTTPEnable: true, + kCFNetworkProxiesHTTPPort: 123, + kCFNetworkProxiesHTTPProxy: "www.example.com", + kCFProxyUsernameKey: "proxyuser", + kCFProxyPasswordKey: "proxypass", ] let client = HTTPClient(proxyConfiguration: proxyConfiguration) let actualProxy: [AnyHashable: Any] = client.session.configuration.connectionProxyDictionary! - XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) - XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) - XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") - XCTAssertEqual(actualProxy[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") - XCTAssertEqual(actualProxy[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPEnable] as? Bool, true) + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPPort] as? Int, 123) + XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") + XCTAssertEqual(actualProxy[kCFProxyUsernameKey] as? String, "proxyuser") + XCTAssertEqual(actualProxy[kCFProxyPasswordKey] as? String, "proxypass") } } diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index 2bf1fa4f4f..fbb3527d68 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -92,11 +92,11 @@ class DatadogConfigurationBuilderTests: XCTestCase { .set(uploadFrequency: .frequent) .set(additionalConfiguration: ["foo": 42, "bar": "something"]) .set(proxyConfiguration: [ - kCFNetworkProxiesHTTPEnable as AnyHashable: true, - kCFNetworkProxiesHTTPPort as AnyHashable: 123, - kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", - kCFProxyUsernameKey as AnyHashable: "proxyuser", - kCFProxyPasswordKey as AnyHashable: "proxypass", + kCFNetworkProxiesHTTPEnable: true, + kCFNetworkProxiesHTTPPort: 123, + kCFNetworkProxiesHTTPProxy: "www.example.com", + kCFProxyUsernameKey: "proxyuser", + kCFProxyPasswordKey: "proxypass", ]) return builder @@ -146,11 +146,11 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertEqual(configuration.uploadFrequency, .frequent) XCTAssertEqual(configuration.additionalConfiguration["foo"] as? Int, 42) XCTAssertEqual(configuration.additionalConfiguration["bar"] as? String, "something") - XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) - XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) - XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") - XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") - XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable] as? Bool, true) + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPPort] as? Int, 123) + XCTAssertEqual(configuration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") + XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyUsernameKey] as? String, "proxyuser") + XCTAssertEqual(configuration.proxyConfiguration?[kCFProxyPasswordKey] as? String, "proxypass") } XCTAssertTrue(rumConfigurationWithDefaultValues.rumUIKitViewsPredicate is DefaultUIKitRUMViewsPredicate) diff --git a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift index 8f235981a1..d3d01d5ee4 100644 --- a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift +++ b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift @@ -181,12 +181,12 @@ class DDConfigurationTests: XCTestCase { XCTAssertEqual(objcBuilder.build().sdkConfiguration.additionalConfiguration["foo"] as? Int, 42) XCTAssertEqual(objcBuilder.build().sdkConfiguration.additionalConfiguration["bar"] as? String, "something") - objcBuilder.set(proxyConfiguration: [kCFNetworkProxiesHTTPEnable as AnyHashable: true, kCFNetworkProxiesHTTPPort as AnyHashable: 123, kCFNetworkProxiesHTTPProxy as AnyHashable: "www.example.com", kCFProxyUsernameKey as AnyHashable: "proxyuser", kCFProxyPasswordKey as AnyHashable: "proxypass" ]) - XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable as AnyHashable] as? Bool, true) - XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPPort as AnyHashable] as? Int, 123) - XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy as AnyHashable] as? String, "www.example.com") - XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyUsernameKey as AnyHashable] as? String, "proxyuser") - XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyPasswordKey as AnyHashable] as? String, "proxypass") + objcBuilder.set(proxyConfiguration: [kCFNetworkProxiesHTTPEnable: true, kCFNetworkProxiesHTTPPort: 123, kCFNetworkProxiesHTTPProxy: "www.example.com", kCFProxyUsernameKey: "proxyuser", kCFProxyPasswordKey: "proxypass" ]) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPEnable] as? Bool, true) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPPort] as? Int, 123) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyUsernameKey] as? String, "proxyuser") + XCTAssertEqual(objcBuilder.build().sdkConfiguration.proxyConfiguration?[kCFProxyPasswordKey] as? String, "proxypass") } func testScrubbingRUMEvents() { From 4432b97999f0f9650b656a6f99ca5df07c316316 Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Fri, 3 Sep 2021 09:00:51 +0200 Subject: [PATCH 29/80] RUMM-1350 add documentation --- docs/rum_collection/advanced_configuration.md | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/rum_collection/advanced_configuration.md b/docs/rum_collection/advanced_configuration.md index 3d1eba7b87..7bb4fc8702 100644 --- a/docs/rum_collection/advanced_configuration.md +++ b/docs/rum_collection/advanced_configuration.md @@ -198,7 +198,7 @@ You can use the following methods in `Datadog.Configuration.Builder` when creati `setSpanEventMapper(_ mapper: @escaping (SpanEvent) -> SpanEvent)` : Sets the data scrubbing callback for spans. This can be used to modify or drop span events before they are sent to Datadog. - + ### Automatically track views To automatically track views (`UIViewControllers`), use the `.trackUIKitRUMViews()` option when configuring the SDK. By default, views are named with the view controller's class name. To customize it, use `.trackUIKitRUMViews(using: predicate)` and provide your own implementation of the `predicate` which conforms to `UIKitRUMViewsPredicate` protocol: @@ -391,6 +391,27 @@ This means that even if users open your application while offline, no data is lo **Note**: The data on the disk is automatically discarded if it gets too old to ensure the SDK doesn't use too much disk space. +## Configuring a custom Proxy for Datadog data upload + +If your app is running on devices behind a custom proxy, you can let the SDK's data uploader know about it to ensure all tracked data are uploaded with the relevant configuration. You can specify your proxy configuration (as described in the [URLSessionConfiguration.connectionProxyDictionary][12] documentation) when initializing the SDK. + +```swift +Datadog.initialize( + // ... + configuration: Datadog.Configuration + .builderUsing(/* ... */) + .set(proxyConfiguration: [ + kCFNetworkProxiesHTTPEnable: true, + kCFNetworkProxiesHTTPPort: 123, + kCFNetworkProxiesHTTPProxy: "www.example.com", + kCFProxyUsernameKey: "proxyuser", + kCFProxyPasswordKey: "proxypass" + ]) + // ... + .build() +) +``` + ## Further Reading @@ -408,3 +429,4 @@ This means that even if users open your application while offline, no data is lo [9]: #modify-or-drop-rum-events [10]: https://docs.datadoghq.com/real_user_monitoring/connect_rum_and_traces?tab=browserrum [11]: /real_user_monitoring/ios/data_collected?tab=session#default-attributes +[12]: https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1411499-connectionproxydictionary From 22e44dec4e1a83a3215c565772db027cf67022d8 Mon Sep 17 00:00:00 2001 From: "Xavier F. Gouchet" Date: Mon, 6 Sep 2021 09:34:13 +0200 Subject: [PATCH 30/80] Update docs/rum_collection/advanced_configuration.md Co-authored-by: Sarina Bloodgood <57639676+sarina-dd@users.noreply.github.com> --- docs/rum_collection/advanced_configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rum_collection/advanced_configuration.md b/docs/rum_collection/advanced_configuration.md index 7bb4fc8702..3e7d66aa06 100644 --- a/docs/rum_collection/advanced_configuration.md +++ b/docs/rum_collection/advanced_configuration.md @@ -391,7 +391,7 @@ This means that even if users open your application while offline, no data is lo **Note**: The data on the disk is automatically discarded if it gets too old to ensure the SDK doesn't use too much disk space. -## Configuring a custom Proxy for Datadog data upload +## Configuring a custom proxy for Datadog data upload If your app is running on devices behind a custom proxy, you can let the SDK's data uploader know about it to ensure all tracked data are uploaded with the relevant configuration. You can specify your proxy configuration (as described in the [URLSessionConfiguration.connectionProxyDictionary][12] documentation) when initializing the SDK. From 93251a4a172ac3d1316acec25fe412f0c6db89a9 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 12:10:00 +0200 Subject: [PATCH 31/80] RUMM-1420 Fix Remove `attributes` parameter from `RUMEventBuilder` --- .../RUM/RUMEvent/RUMEventBuilder.swift | 14 ++------- .../RUMMonitor/Scopes/RUMResourceScope.swift | 8 ++--- .../Scopes/RUMUserActionScope.swift | 4 +-- .../RUM/RUMMonitor/Scopes/RUMViewScope.swift | 12 ++++---- .../RUM/RUMEvent/RUMEventBuilderTests.swift | 30 ++----------------- .../RUMEventFileOutputTests.swift | 4 +-- .../RUMMonitor/Scopes/RUMViewScopeTests.swift | 4 +-- 7 files changed, 21 insertions(+), 55 deletions(-) diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift index 7c8638066c..f35f4c5e42 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventBuilder.swift @@ -13,18 +13,8 @@ internal class RUMEventBuilder { self.eventsMapper = eventsMapper } - func createRUMEvent( - with model: DM, - attributes: [String: Encodable] - ) -> RUMEvent? { - var model = model - - if !attributes.isEmpty { - model.context = RUMEventAttributes(contextInfo: attributes) - } - + func createRUMEvent(with model: DM) -> RUMEvent? { let event = RUMEvent(model: model) - let mappedEvent = eventsMapper.map(event: event) - return mappedEvent + return eventsMapper.map(event: event) } } diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift index f82379f6ee..11575623fe 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift @@ -139,7 +139,7 @@ internal class RUMResourceScope: RUMScope { }, application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: resourceStartTime).timeIntervalSince1970.toInt64Milliseconds, resource: .init( connect: resourceMetrics?.connect.flatMap { metric in @@ -198,7 +198,7 @@ internal class RUMResourceScope: RUMScope { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) return true } @@ -217,7 +217,7 @@ internal class RUMResourceScope: RUMScope { }, application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: command.time).timeIntervalSince1970.toInt64Milliseconds, error: .init( handling: nil, @@ -247,7 +247,7 @@ internal class RUMResourceScope: RUMScope { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) return true } diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift index 4bcf635405..b9cf66a0b7 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift @@ -143,7 +143,7 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { ), application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: actionStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), @@ -157,7 +157,7 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) return true } diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift index 34bf4691f3..cf83ecfc62 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift @@ -302,7 +302,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ), application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: viewStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), @@ -316,7 +316,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: command.attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) return true } @@ -340,7 +340,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ), application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: viewStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), @@ -381,7 +381,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) crashContextIntegration?.update(lastRUMViewEvent: event) } else { @@ -401,7 +401,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { }, application: .init(id: context.rumApplicationID), connectivity: dependencies.connectivityInfoProvider.current, - context: nil, + context: .init(contextInfo: attributes), date: dateCorrection.applying(to: command.time).timeIntervalSince1970.toInt64Milliseconds, error: .init( handling: nil, @@ -426,7 +426,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ) ) - if let event = dependencies.eventBuilder.createRUMEvent(with: eventData, attributes: attributes) { + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { dependencies.eventOutput.write(rumEvent: event) return true } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift index d1cc54358a..6e189e3a84 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventBuilderTests.swift @@ -8,21 +8,6 @@ import XCTest @testable import Datadog class RUMEventBuilderTests: XCTestCase { - func testItBuildsRUMEvent() throws { - let builder = RUMEventBuilder(eventsMapper: .mockNoOp()) - - let event = try XCTUnwrap( - builder.createRUMEvent( - with: RUMDataModelMock(attribute: "foo"), - attributes: ["foo": "bar", "fizz": "buzz"] - ) - ) - - XCTAssertEqual(event.model.attribute, "foo") - XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["foo"], "bar") - XCTAssertEqual((event.model.context?.contextInfo as? [String: String])?["fizz"], "buzz") - } - func testGivenEventBuilderWithEventMapper_whenEventIsModified_itBuildsModifiedEvent() throws { let builder = RUMEventBuilder( eventsMapper: .mockWith( @@ -33,10 +18,7 @@ class RUMEventBuilderTests: XCTestCase { ) let originalEventModel = RUMViewEvent.mockRandom() let event = try XCTUnwrap( - builder.createRUMEvent( - with: RUMViewEvent.mockRandom(), - attributes: ["foo": "bar", "fizz": "buzz"] - ) + builder.createRUMEvent(with: RUMViewEvent.mockRandom()) ) XCTAssertNotEqual(event.model, originalEventModel) } @@ -49,10 +31,7 @@ class RUMEventBuilderTests: XCTestCase { } ) ) - let event = builder.createRUMEvent( - with: RUMResourceEvent.mockRandom(), - attributes: [:] - ) + let event = builder.createRUMEvent(with: RUMResourceEvent.mockRandom()) XCTAssertNil(event) } @@ -66,10 +45,7 @@ class RUMEventBuilderTests: XCTestCase { ) let originalEventModel = RUMResourceEvent.mockRandom() let event = try XCTUnwrap( - builder.createRUMEvent( - with: originalEventModel, - attributes: [:] - ) + builder.createRUMEvent(with: originalEventModel) ) XCTAssertEqual(event.model, originalEventModel) } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift index 654314294a..545e918938 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEventOutputs/RUMEventFileOutputTests.swift @@ -37,8 +37,8 @@ class RUMEventFileOutputTests: XCTestCase { let dataModel1 = RUMDataModelMock(attribute: "foo", context: RUMEventAttributes(contextInfo: ["custom.attribute": "value"])) let dataModel2 = RUMDataModelMock(attribute: "bar") - let event1 = try XCTUnwrap(builder.createRUMEvent(with: dataModel1, attributes: ["custom.attribute": "value"])) - let event2 = try XCTUnwrap(builder.createRUMEvent(with: dataModel2, attributes: [:])) + let event1 = try XCTUnwrap(builder.createRUMEvent(with: dataModel1)) + let event2 = try XCTUnwrap(builder.createRUMEvent(with: dataModel2)) output.write(rumEvent: event1) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index ee6ee1b781..fc38d38c21 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -178,7 +178,7 @@ class RUMViewScopeTests: XCTestCase { identity: mockView, path: "UIViewController", name: "ViewName", - attributes: [:], + attributes: ["foo": "bar"], customTimings: [:], startTime: currentTime ) @@ -217,7 +217,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(event.model.view.error.count, 0) XCTAssertEqual(event.model.view.resource.count, 0) XCTAssertEqual(event.model.dd.documentVersion, 2) - XCTAssertNil(event.model.context) + XCTAssertEqual(event.model.context?.contextInfo as? [String: String], ["foo": "bar"]) } func testWhenAnotherViewIsStarted_itEndsTheScope() throws { From a601df603dae30fec4e95eb56a5c4bb340876a1b Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 12:10:16 +0200 Subject: [PATCH 32/80] RUMM-1420 Remove TODO --- .../Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift index f3602ceda4..6661d9f588 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMUserInfoProviderTests.swift @@ -31,7 +31,6 @@ class RUMUserInfoProviderTests: XCTestCase { let extraInfo: [String: Encodable] = mockRandomAttributes() userInfoProvider.value = UserInfo(id: "abc-123", name: "Foo", email: "foo@bar.com", extraInfo: extraInfo) - // TODO: RUMM-1420 Encode user `extraInfo` info as RUMUser `usrInfo` XCTAssertEqual(rumUserInfoProvider.current, RUMUser(email: "foo@bar.com", id: "abc-123", name: "Foo", usrInfo: extraInfo)) } } From f9a7a4c0d54c8b446f9503afb2e7f0eb102fbce7 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 13:55:31 +0200 Subject: [PATCH 33/80] RUMM-1420 Remove error attributes form `CodableRUMViewEvent` --- .../CrashReporting/CrashContext/CrashContext.swift | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift b/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift index b1b9c68690..ddd037224c 100644 --- a/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift +++ b/Sources/Datadog/CrashReporting/CrashContext/CrashContext.swift @@ -150,35 +150,29 @@ private struct CodableUserInfo: Codable { private struct CodableRUMViewEvent: Codable { private let model: RUMViewEvent - private let errorAttributes: [AttributeKey: AttributeValue]? init(from managedValue: RUMEvent) { self.model = managedValue.model - self.errorAttributes = managedValue.errorAttributes } var managedValue: RUMEvent { - return .init(model: model, errorAttributes: errorAttributes) + return .init(model: model) } // MARK: - Codable enum CodingKeys: String, CodingKey { case model = "mdl" - case errorAttributes = "ea" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.model = try container.decode(RUMViewEvent.self, forKey: .model) - self.errorAttributes = try container.decodeIfPresent([String: CodableValue].self, forKey: .errorAttributes) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(model, forKey: .model) - let error = errorAttributes?.mapValues { CodableValue($0) } - try container.encode(error, forKey: .errorAttributes) } } From 9e8e442e848da15fcb7bb424249f3847210e520c Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 14:00:31 +0200 Subject: [PATCH 34/80] RUMM-1420 make ValuePublisher final --- Sources/Datadog/Core/Utils/ValuePublisher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Datadog/Core/Utils/ValuePublisher.swift b/Sources/Datadog/Core/Utils/ValuePublisher.swift index 469a50d810..0217ab01f5 100644 --- a/Sources/Datadog/Core/Utils/ValuePublisher.swift +++ b/Sources/Datadog/Core/Utils/ValuePublisher.swift @@ -19,7 +19,7 @@ internal protocol ValueObserver: AnyObject { } /// Manages the `Value` in a thread safe manner and notifies subscribed `ValueObservers` on its change. -internal class ValuePublisher { +internal final class ValuePublisher { /// Type erasure for `ValueObserver` type. private struct AnyObserver { let notifyValueChanged: (ObservedValue, ObservedValue) -> Void From 677d8a3d7f5a7c7746d0bc2f5ea002d838c7a94d Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Mon, 6 Sep 2021 16:48:22 +0200 Subject: [PATCH 35/80] RUMM-1597 Add tests to assert that DDCR format matches PLCR text format --- Datadog/Datadog.xcodeproj/project.pbxproj | 2 + .../PLCrashReporterIntegration.swift | 23 ++++--- .../DDCrashReportExporterTests.swift | 69 +++++++++++++++++++ 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index bf490d330b..9e2e94e98c 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -297,6 +297,7 @@ 61993668265BBEDC009D7EA8 /* E2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61993667265BBEDC009D7EA8 /* E2ETests.swift */; }; 61993673265BC371009D7EA8 /* Kronos.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; }; 61993674265BC371009D7EA8 /* Kronos.xcframework in ⚙️ Embed Framework Dependencies */ = {isa = PBXBuildFile; fileRef = 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 619A29F326E64910007D62A3 /* CrashReporter.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 614ED36B260352DC00C8C519 /* CrashReporter.xcframework */; }; 619E16D82577C1CB00B2516B /* DataProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619E16D72577C1CB00B2516B /* DataProcessor.swift */; }; 619E16E92578E73E00B2516B /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619E16E82578E73E00B2516B /* DataMigrator.swift */; }; 619E16F12578E89700B2516B /* DeleteAllDataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 619E16F02578E89700B2516B /* DeleteAllDataMigrator.swift */; }; @@ -1226,6 +1227,7 @@ buildActionMask = 2147483647; files = ( 61B7885D25C180CB002675B5 /* DatadogCrashReporting.framework in Frameworks */, + 619A29F326E64910007D62A3 /* CrashReporter.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/DatadogCrashReporting/PLCrashReporterIntegration/PLCrashReporterIntegration.swift b/Sources/DatadogCrashReporting/PLCrashReporterIntegration/PLCrashReporterIntegration.swift index a0173e1e14..6a71250dcc 100644 --- a/Sources/DatadogCrashReporting/PLCrashReporterIntegration/PLCrashReporterIntegration.swift +++ b/Sources/DatadogCrashReporting/PLCrashReporterIntegration/PLCrashReporterIntegration.swift @@ -7,20 +7,25 @@ import CrashReporter import Datadog +internal extension PLCrashReporterConfig { + /// `PLCR` configuration used for `DatadogCrashReporting` + static func ddConfiguration() -> PLCrashReporterConfig { + return PLCrashReporterConfig( + // The choice of `.BSD` over `.mach` is well discussed here: + // https://github.com/ChatSecure/PLCrashReporter/blob/7f27b272d5ff0d6650fc41317127bb2378ed6e88/Source/CrashReporter.h#L238-L363 + signalHandlerType: .BSD, + // We don't symbolicate on device. All symbolication will happen backend-side. + symbolicationStrategy: [] + ) + } +} + internal final class PLCrashReporterIntegration: ThirdPartyCrashReporter { private let crashReporter: PLCrashReporter private let builder = DDCrashReportBuilder() init() throws { - self.crashReporter = PLCrashReporter( - configuration: PLCrashReporterConfig( - // The choice of `.BSD` over `.mach` is well discussed here: - // https://github.com/ChatSecure/PLCrashReporter/blob/7f27b272d5ff0d6650fc41317127bb2378ed6e88/Source/CrashReporter.h#L238-L363 - signalHandlerType: .BSD, - // We don't symbolicate on device. All symbolication will happen backend-side. - symbolicationStrategy: [] - ) - ) + self.crashReporter = PLCrashReporter(configuration: .ddConfiguration()) try crashReporter.enableAndReturnError() } diff --git a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportExporterTests.swift b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportExporterTests.swift index ff0c986174..a1a72767a8 100644 --- a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportExporterTests.swift +++ b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportExporterTests.swift @@ -7,6 +7,7 @@ import XCTest @testable import Datadog @testable import DatadogCrashReporting +import CrashReporter class DDCrashReportExporterTests: XCTestCase { private let exporter = DDCrashReportExporter() @@ -356,4 +357,72 @@ class DDCrashReportExporterTests: XCTestCase { crashReport.wasTruncated = randomFlag XCTAssertEqual(exporter.export(crashReport).wasTruncated, randomFlag) } + + // MARK: - Comparing with PLCR text format + + func testExportedStacksHaveTheSameFormatAndValuesAsIfTheyWereExportedFromPLCR() throws { + let crashReporter = PLCrashReporter(configuration: .ddConfiguration())! + + // Given + let plCrashReport = try PLCrashReport( + data: try crashReporter.generateLiveReportAndReturnError() + ) + + // When + let exporter = DDCrashReportExporter() + let ddCrashReport = exporter.export(try CrashReport(from: plCrashReport)) + + // Then + let plcrTextFormat = PLCrashReportTextFormatter.stringValue(for: plCrashReport, with: PLCrashReportTextFormatiOS)! + + ddCrashReport.threads.forEach { thread in + XCTAssertTrue( + plcrTextFormat.contains(thread.stack), + """ + Stack: + ``` + \(thread.stack) + ``` + + does not appear in PLCR text format: + ``` + \(plcrTextFormat) + ``` + """ + ) + } + } + + func testExportedBinaryImagesHaveTheSameValuesAsIfTheyWereExportedFromPLCR() throws { + let crashReporter = PLCrashReporter(configuration: .ddConfiguration())! + + // Given + let plCrashReport = try PLCrashReport( + data: try crashReporter.generateLiveReportAndReturnError() + ) + + // When + let exporter = DDCrashReportExporter() + let ddCrashReport = exporter.export(try CrashReport(from: plCrashReport)) + + // Then + let plcrTextFormat = PLCrashReportTextFormatter.stringValue(for: plCrashReport, with: PLCrashReportTextFormatiOS)! + let plcrByLines = plcrTextFormat.split(separator: "\n").reversed() // matching in reversed report is 2x faster + + ddCrashReport.binaryImages.forEach { binaryImage in + XCTAssertTrue( + plcrByLines.contains { line in + // PLCR uses free-form text format, e.g.: + // ` 0x10ce2e000 - 0x10ced9fff +Example x86_64 /.../Example.app/Example` + // Instead of matching the whole line, just checking if all values appear in the line should be enough: + let matchLoadAddress = line.contains(binaryImage.loadAddress) + let matchMaxAddress = line.contains(binaryImage.maxAddress) + let matchArchitecture = line.contains(binaryImage.architecture) + let matchLibraryName = line.contains(binaryImage.libraryName) + let matchUUID = line.contains(binaryImage.uuid) + return matchLoadAddress && matchMaxAddress && matchArchitecture && matchLibraryName && matchUUID + } + ) + } + } } From 5fcacc1665ab1fada3c3b87509c09c221f7b1f74 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 19:57:11 +0200 Subject: [PATCH 36/80] RUMM-1455 Add onSessionStart callback setter --- .../Datadog/Core/FeaturesConfiguration.swift | 4 +++- Sources/Datadog/DatadogConfiguration.swift | 13 ++++++++++++ Sources/Datadog/RUM/RUMFeature.swift | 10 ++++++++-- .../Scopes/RUMApplicationScope.swift | 12 +++++++++++ .../RUMMonitor/Scopes/RUMSessionScope.swift | 4 ++-- Sources/Datadog/RUMMonitor.swift | 3 ++- .../DatadogConfiguration+objc.swift | 5 +++++ .../Datadog/Mocks/CoreMocks.swift | 6 ++++-- .../Datadog/Mocks/RUMFeatureMocks.swift | 16 +++++++++++---- .../Datadog/RUMMonitorTests.swift | 20 +++++++++++++++++++ 10 files changed, 81 insertions(+), 12 deletions(-) diff --git a/Sources/Datadog/Core/FeaturesConfiguration.swift b/Sources/Datadog/Core/FeaturesConfiguration.swift index 0e9ac177cc..65a9ff9205 100644 --- a/Sources/Datadog/Core/FeaturesConfiguration.swift +++ b/Sources/Datadog/Core/FeaturesConfiguration.swift @@ -53,6 +53,7 @@ internal struct FeaturesConfiguration { /// RUM auto instrumentation configuration, `nil` if not enabled. let autoInstrumentation: AutoInstrumentation? let backgroundEventTrackingEnabled: Bool + let onSessionStart: RUMSessionListener? } struct URLSessionAutoInstrumentation { @@ -200,7 +201,8 @@ extension FeaturesConfiguration { actionEventMapper: configuration.rumActionEventMapper, errorEventMapper: configuration.rumErrorEventMapper, autoInstrumentation: autoInstrumentation, - backgroundEventTrackingEnabled: configuration.rumBackgroundEventTrackingEnabled + backgroundEventTrackingEnabled: configuration.rumBackgroundEventTrackingEnabled, + onSessionStart: configuration.rumSessionsListener ) } else { let error = ProgrammerError( diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index 1b8c954c8f..a303218a63 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -233,6 +233,7 @@ extension Datadog { private(set) var firstPartyHosts: Set? private(set) var spanEventMapper: SpanEventMapper? private(set) var rumSessionsSamplingRate: Float + private(set) var rumSessionsListener: RUMSessionListener? private(set) var rumUIKitViewsPredicate: UIKitRUMViewsPredicate? private(set) var rumUIKitUserActionsPredicate: UIKitRUMUserActionsPredicate? private(set) var rumViewEventMapper: RUMViewEventMapper? @@ -306,6 +307,7 @@ extension Datadog { firstPartyHosts: nil, spanEventMapper: nil, rumSessionsSamplingRate: 100.0, + rumSessionsListener: nil, rumUIKitViewsPredicate: nil, rumUIKitUserActionsPredicate: nil, rumViewEventMapper: nil, @@ -505,6 +507,17 @@ extension Datadog { return self } + /// Sets the Session start callback. + /// + /// The callback takes 2 arguments: the newly started Session ID and a boolean indicating whether or not the session is discarded by the sampling rate + /// (when `true` it means no event in this session will be kept). + /// + /// - Parameter handler: the callback handler to notify whenever a new Session starts. + public func onSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder { + configuration.rumSessionsListener = handler + return self + } + /// Sets the predicate for automatically tracking `UIViewControllers` as RUM Views. /// /// When the app is running, the SDK will ask provided `predicate` if any noticed `UIViewController` should be considered diff --git a/Sources/Datadog/RUM/RUMFeature.swift b/Sources/Datadog/RUM/RUMFeature.swift index 0da8e5b1f4..ea504091f5 100644 --- a/Sources/Datadog/RUM/RUMFeature.swift +++ b/Sources/Datadog/RUM/RUMFeature.swift @@ -41,6 +41,8 @@ internal final class RUMFeature { let vitalMemoryReader: SamplingBasedVitalReader // VitalMemoryReader let vitalRefreshRateReader: ContinuousVitalReader // VitalRefreshRateReader + let onSessionStart: RUMSessionListener? + // MARK: - Components static let featureName = "rum" @@ -143,7 +145,8 @@ internal final class RUMFeature { commonDependencies: commonDependencies, vitalCPUReader: VitalCPUReader(), vitalMemoryReader: VitalMemoryReader(), - vitalRefreshRateReader: VitalRefreshRateReader() + vitalRefreshRateReader: VitalRefreshRateReader(), + onSessionStart: configuration.onSessionStart ) } @@ -155,7 +158,8 @@ internal final class RUMFeature { commonDependencies: FeaturesCommonDependencies, vitalCPUReader: SamplingBasedVitalReader, vitalMemoryReader: SamplingBasedVitalReader, - vitalRefreshRateReader: ContinuousVitalReader + vitalRefreshRateReader: ContinuousVitalReader, + onSessionStart: RUMSessionListener? = nil ) { // Configuration self.configuration = configuration @@ -176,6 +180,8 @@ internal final class RUMFeature { self.vitalCPUReader = vitalCPUReader self.vitalMemoryReader = vitalMemoryReader self.vitalRefreshRateReader = vitalRefreshRateReader + + self.onSessionStart = onSessionStart } #if DD_SDK_COMPILED_FOR_TESTING diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift index 6bc69d0574..ea4a35826e 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScope.swift @@ -6,6 +6,8 @@ import Foundation +internal typealias RUMSessionListener = (String, Bool) -> Void + /// Injection container for common dependencies used by all `RUMScopes`. internal struct RUMScopeDependencies { let userInfoProvider: RUMUserInfoProvider @@ -20,6 +22,8 @@ internal struct RUMScopeDependencies { let vitalCPUReader: SamplingBasedVitalReader let vitalMemoryReader: SamplingBasedVitalReader let vitalRefreshRateReader: ContinuousVitalReader + + let onSessionStart: RUMSessionListener? } internal class RUMApplicationScope: RUMScope, RUMContextProvider { @@ -28,6 +32,7 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { /// Session scope. It gets created with the first `.startView` event. /// Might be re-created later according to session duration constraints. private(set) var sessionScope: RUMSessionScope? + /// RUM Sessions sampling rate. internal let samplingRate: Float @@ -87,6 +92,7 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { private func refresh(expiredSession: RUMSessionScope, on command: RUMCommand) { let refreshedSession = RUMSessionScope(from: expiredSession, startTime: command.time) sessionScope = refreshedSession + sessionScopeDidUpdate(refreshedSession) _ = refreshedSession.process(command: command) } @@ -103,6 +109,12 @@ internal class RUMApplicationScope: RUMScope, RUMContextProvider { ) sessionScope = initialSession + sessionScopeDidUpdate(initialSession) _ = initialSession.process(command: startInitialViewCommand) } + + private func sessionScopeDidUpdate(_ sessionScope: RUMSessionScope) { + let sessionID = sessionScope.sessionUUID.rawValue.uuidString + dependencies.onSessionStart?(sessionID, sessionScope.shouldBeSampledOut) + } } diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift index c163ddc74e..fe71d769d9 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScope.swift @@ -30,10 +30,10 @@ internal class RUMSessionScope: RUMScope, RUMContextProvider { /// This Session UUID. Equals `.nullUUID` if the Session is sampled. let sessionUUID: RUMUUID + /// Tells if events from this Session should be sampled-out (not send). + let shouldBeSampledOut: Bool /// RUM Session sampling rate. private let samplingRate: Float - /// Tells if events from this Session should be sampled-out (not send). - private let shouldBeSampledOut: Bool /// The start time of this Session. private let sessionStartTime: Date /// Time of the last RUM interaction noticed by this Session. diff --git a/Sources/Datadog/RUMMonitor.swift b/Sources/Datadog/RUMMonitor.swift index 01f2c4ac54..e6defbb786 100644 --- a/Sources/Datadog/RUMMonitor.swift +++ b/Sources/Datadog/RUMMonitor.swift @@ -190,7 +190,8 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber { dateCorrector: rumFeature.dateCorrector, vitalCPUReader: rumFeature.vitalCPUReader, vitalMemoryReader: rumFeature.vitalMemoryReader, - vitalRefreshRateReader: rumFeature.vitalRefreshRateReader + vitalRefreshRateReader: rumFeature.vitalRefreshRateReader, + onSessionStart: rumFeature.onSessionStart ), samplingRate: rumFeature.configuration.sessionSamplingRate, backgroundEventTrackingEnabled: rumFeature.configuration.backgroundEventTrackingEnabled diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index 0cb0aea277..44f845338e 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -254,6 +254,11 @@ public class DDConfigurationBuilder: NSObject { _ = sdkBuilder.set(rumSessionsSamplingRate: rumSessionsSamplingRate) } + @objc + public func set(onSessionStart handler: @escaping (String, Bool) -> Void) { + _ = sdkBuilder.onSessionStart(handler) + } + @objc public func trackUIKitRUMViews() { let defaultPredicate = DefaultUIKitRUMViewsPredicate() diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index ed989fb31a..c0c6451c1b 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -233,7 +233,8 @@ extension FeaturesConfiguration.RUM { actionEventMapper: RUMActionEventMapper? = nil, errorEventMapper: RUMErrorEventMapper? = nil, autoInstrumentation: FeaturesConfiguration.RUM.AutoInstrumentation? = nil, - backgroundEventTrackingEnabled: Bool = false + backgroundEventTrackingEnabled: Bool = false, + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> Self { return .init( common: common, @@ -246,7 +247,8 @@ extension FeaturesConfiguration.RUM { actionEventMapper: actionEventMapper, errorEventMapper: errorEventMapper, autoInstrumentation: autoInstrumentation, - backgroundEventTrackingEnabled: backgroundEventTrackingEnabled + backgroundEventTrackingEnabled: backgroundEventTrackingEnabled, + onSessionStart: onSessionStart ) } } diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index f7b5215930..316c1c56b9 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -401,6 +401,10 @@ extension RUMContext { // MARK: - RUMScope Mocks +func mockNoOpSessionListerner() -> RUMSessionListener { + return { _, _ in } +} + extension RUMScopeDependencies { static func mockAny() -> RUMScopeDependencies { return mockWith() @@ -416,7 +420,8 @@ extension RUMScopeDependencies { eventBuilder: RUMEventBuilder = RUMEventBuilder(userInfoProvider: UserInfoProvider.mockAny(), eventsMapper: RUMEventsMapper.mockNoOp()), eventOutput: RUMEventOutput = RUMEventOutputMock(), rumUUIDGenerator: RUMUUIDGenerator = DefaultRUMUUIDGenerator(), - dateCorrector: DateCorrectorType = DateCorrectorMock() + dateCorrector: DateCorrectorType = DateCorrectorMock(), + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> RUMScopeDependencies { return RUMScopeDependencies( userInfoProvider: userInfoProvider, @@ -428,7 +433,8 @@ extension RUMScopeDependencies { dateCorrector: dateCorrector, vitalCPUReader: SamplingBasedVitalReaderMock(), vitalMemoryReader: SamplingBasedVitalReaderMock(), - vitalRefreshRateReader: ContinuousVitalReaderMock() + vitalRefreshRateReader: ContinuousVitalReaderMock(), + onSessionStart: onSessionStart ) } @@ -440,7 +446,8 @@ extension RUMScopeDependencies { eventBuilder: RUMEventBuilder? = nil, eventOutput: RUMEventOutput? = nil, rumUUIDGenerator: RUMUUIDGenerator? = nil, - dateCorrector: DateCorrectorType? = nil + dateCorrector: DateCorrectorType? = nil, + onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() ) -> RUMScopeDependencies { return RUMScopeDependencies( userInfoProvider: userInfoProvider ?? self.userInfoProvider, @@ -452,7 +459,8 @@ extension RUMScopeDependencies { dateCorrector: dateCorrector ?? self.dateCorrector, vitalCPUReader: SamplingBasedVitalReaderMock(), vitalMemoryReader: SamplingBasedVitalReaderMock(), - vitalRefreshRateReader: ContinuousVitalReaderMock() + vitalRefreshRateReader: ContinuousVitalReaderMock(), + onSessionStart: onSessionStart ) } } diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index eec87c6e70..efefa57a94 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -785,6 +785,26 @@ class RUMMonitorTests: XCTestCase { XCTAssertEqual(try lastViewUpdate.timing(named: "timing3_.@$-______"), 3_000_000_000) } + // MARK: - RUM New Session + + func testStartingViewCreatesNewSession() { + RUMFeature.instance = .mockWith( + directories: temporaryFeatureDirectories, + configuration: .mockWith( + sessionSamplingRate: 100, + onSessionStart: { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertFalse(isDiscarded) + } + ) + ) + + defer { RUMFeature.instance?.deinitialize() } + + let monitor = RUMMonitor.initialize() + monitor.startView(viewController: mockView) + } + // MARK: - RUM Events Dates Correction func testGivenTimeDifferenceBetweenDeviceAndServer_whenCollectingRUMEvents_thenEventsDateUseServerTime() throws { From 840ba8d6ba70a6bc88a42729304833acaf11c8b2 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 6 Sep 2021 19:57:31 +0200 Subject: [PATCH 37/80] RUMM-1455 API surface --- api-surface-objc | 33 +++++++++++++++++++++++++++++ api-surface-swift | 54 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/api-surface-objc b/api-surface-objc index dadb7a954c..994267c5c9 100644 --- a/api-surface-objc +++ b/api-surface-objc @@ -15,6 +15,7 @@ public class DDDatadog: NSObject public static func verbosityLevel() -> DDSDKVerbosityLevel public static func setUserInfo(id: String? = nil, name: String? = nil, email: String? = nil, extraInfo: [String: Any] = [:]) public static func setTrackingConsent(consent: DDTrackingConsent) + public static func flushAndDeinitialize() public class DDEndpoint: NSObject public static func us1() -> DDEndpoint public static func us3() -> DDEndpoint @@ -68,6 +69,7 @@ public class DDConfigurationBuilder: NSObject public func trackURLSession(firstPartyHosts: Set) public func set(serviceName: String) public func set(rumSessionsSamplingRate: Float) + public func set(onSessionStart handler: @escaping (String, Bool) -> Void) public func trackUIKitRUMViews() public func trackUIKitRUMViews(using predicate: DDUIKitRUMViewsPredicate) public func trackUIKitActions() @@ -80,6 +82,7 @@ public class DDConfigurationBuilder: NSObject public func set(batchSize: DDBatchSize) public func set(uploadFrequency: DDUploadFrequency) public func set(additionalConfiguration: [String: Any]) + public func set(proxyConfiguration: [AnyHashable: Any]) public func build() -> DDConfiguration public class DDGlobal: NSObject @objc public static var sharedTracer = DatadogObjc.DDTracer(swiftTracer: Datadog.Global.sharedTracer) @@ -167,6 +170,12 @@ public class DDRUMViewEvent: NSObject public class DDRUMViewEventDD: NSObject @objc public var documentVersion: NSNumber @objc public var formatVersion: NSNumber + @objc public var session: DDRUMViewEventDDSession? +public class DDRUMViewEventDDSession: NSObject + @objc public var plan: DDRUMViewEventDDSessionPlan +public enum DDRUMViewEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMViewEventApplication: NSObject @objc public var id: String public class DDRUMViewEventRUMConnectivity: NSObject @@ -273,8 +282,14 @@ public class DDRUMResourceEvent: NSObject @objc public var view: DDRUMResourceEventView public class DDRUMResourceEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMResourceEventDDSession? @objc public var spanId: String? @objc public var traceId: String? +public class DDRUMResourceEventDDSession: NSObject + @objc public var plan: DDRUMResourceEventDDSessionPlan +public enum DDRUMResourceEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMResourceEventAction: NSObject @objc public var id: String public class DDRUMResourceEventApplication: NSObject @@ -405,6 +420,12 @@ public class DDRUMActionEvent: NSObject @objc public var view: DDRUMActionEventView public class DDRUMActionEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMActionEventDDSession? +public class DDRUMActionEventDDSession: NSObject + @objc public var plan: DDRUMActionEventDDSessionPlan +public enum DDRUMActionEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMActionEventAction: NSObject @objc public var crash: DDRUMActionEventActionCrash? @objc public var error: DDRUMActionEventActionError? @@ -490,6 +511,12 @@ public class DDRUMErrorEvent: NSObject @objc public var view: DDRUMErrorEventView public class DDRUMErrorEventDD: NSObject @objc public var formatVersion: NSNumber + @objc public var session: DDRUMErrorEventDDSession? +public class DDRUMErrorEventDDSession: NSObject + @objc public var plan: DDRUMErrorEventDDSessionPlan +public enum DDRUMErrorEventDDSessionPlan: Int + case plan1 + case plan2 public class DDRUMErrorEventAction: NSObject @objc public var id: String public class DDRUMErrorEventApplication: NSObject @@ -518,6 +545,8 @@ public enum DDRUMErrorEventRUMConnectivityStatus: Int public class DDRUMErrorEventRUMEventAttributes: NSObject @objc public var contextInfo: [String: Any] public class DDRUMErrorEventError: NSObject + @objc public var handling: DDRUMErrorEventErrorHandling + @objc public var handlingStack: String? @objc public var id: String? @objc public var isCrash: NSNumber? @objc public var message: String @@ -525,6 +554,10 @@ public class DDRUMErrorEventError: NSObject @objc public var source: DDRUMErrorEventErrorSource @objc public var stack: String? @objc public var type: String? +public enum DDRUMErrorEventErrorHandling: Int + case none + case handled + case unhandled public class DDRUMErrorEventErrorResource: NSObject @objc public var method: DDRUMErrorEventErrorResourceRUMMethod @objc public var provider: DDRUMErrorEventErrorResourceProvider? diff --git a/api-surface-swift b/api-surface-swift index 008962891a..0b843f30bf 100644 --- a/api-surface-swift +++ b/api-surface-swift @@ -81,6 +81,7 @@ public class Datadog public static func initialize(appContext: AppContext,trackingConsent: TrackingConsent,configuration: Configuration) public static var verbosityLevel: LogLevel? = nil public static var debugRUM = false + public static var isInitialized: Bool public static func setUserInfo(id: String? = nil,name: String? = nil,email: String? = nil,extraInfo: [AttributeKey: AttributeValue] = [:]) public static func set(trackingConsent: TrackingConsent) public static func flushAndDeinitialize() @@ -146,6 +147,7 @@ public class Datadog public func enableRUM(_ enabled: Bool) -> Builder public func set(rumEndpoint: RUMEndpoint) -> Builder public func set(rumSessionsSamplingRate: Float) -> Builder + public func onSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder public func trackUIKitRUMViews(using predicate: UIKitRUMViewsPredicate = DefaultUIKitRUMViewsPredicate()) -> Builder public func trackUIKitActions(_ enabled: Bool = true) -> Builder public func trackUIKitRUMActions(using predicate: UIKitRUMUserActionsPredicate = DefaultUIKitRUMUserActionsPredicate()) -> Builder @@ -160,6 +162,7 @@ public class Datadog public func set(serviceName: String) -> Builder public func set(batchSize: BatchSize) -> Builder public func set(uploadFrequency: UploadFrequency) -> Builder + public func set(proxyConfiguration: [AnyHashable: Any]?) -> Builder public func set(additionalConfiguration: [String: Any]) -> Builder public func build() -> Configuration public typealias DDGlobal = Global @@ -295,16 +298,22 @@ public struct RUMViewEvent: RUMDataModel public let dd: DD public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public let service: String? public let session: Session public let type: String = "view" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let documentVersion: Int64 public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Application: Codable public let id: String public struct Session: Codable @@ -372,18 +381,24 @@ public struct RUMResourceEvent: RUMDataModel public let action: Action? public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public var resource: Resource public let service: String? public let session: Session public let type: String = "resource" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? public let spanId: String? public let traceId: String? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let id: String public struct Application: Codable @@ -468,15 +483,21 @@ public struct RUMActionEvent: RUMDataModel public var action: Action public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public let service: String? public let session: Session public let type: String = "action" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let crash: Crash? public let error: Error? @@ -524,21 +545,29 @@ public struct RUMErrorEvent: RUMDataModel public let action: Action? public let application: Application public let connectivity: RUMConnectivity? - public let context: RUMEventAttributes? + public internal(set) var context: RUMEventAttributes? public let date: Int64 public var error: Error public let service: String? public let session: Session public let type: String = "error" - public let usr: RUMUser? + public internal(set) var usr: RUMUser? public var view: View public struct DD: Codable public let formatVersion: Int64 = 2 + public let session: Session? + public struct Session: Codable + public let plan: Plan + public enum Plan: Int, Codable + case plan1 = 1 + case plan2 = 2 public struct Action: Codable public let id: String public struct Application: Codable public let id: String public struct Error: Codable + public let handling: Handling? + public let handlingStack: String? public let id: String? public let isCrash: Bool? public var message: String @@ -546,6 +575,9 @@ public struct RUMErrorEvent: RUMDataModel public let source: Source public var stack: String? public let type: String? + public enum Handling: String, Codable + case handled = "handled" + case unhandled = "unhandled" public struct Resource: Codable public let method: RUMMethod public let provider: Provider? @@ -613,14 +645,14 @@ public struct RUMConnectivity: Codable case notConnected = "not_connected" case maybe = "maybe" public struct RUMEventAttributes: Codable - public let contextInfo: [String: Codable] + public internal(set) var contextInfo: [String: Encodable] public func encode(to encoder: Encoder) throws public init(from decoder: Decoder) throws public struct RUMUser: Codable public let email: String? public let id: String? public let name: String? - public let usrInfo: [String: Codable] + public internal(set) var usrInfo: [String: Encodable] public func encode(to encoder: Encoder) throws public init(from decoder: Decoder) throws public enum RUMMethod: String, Codable @@ -684,8 +716,8 @@ public class Tracer: OTTracer public var bundleWithRUM: Bool public init(serviceName: String? = nil,sendNetworkInfo: Bool = false,bundleWithRUM: Bool = true,globalTags: [String: Encodable]? = nil) public class HTTPHeadersWriter: OTHTTPHeadersWriter - public init() public private(set) var tracePropagationHTTPHeaders: [String: String] = [:] + public init() public func inject(spanContext: OTSpanContext) public struct SpanEvent: Encodable public var operationName: String From b2de625e10cb1567553c4a17a6d02daf6ea79f59 Mon Sep 17 00:00:00 2001 From: maxep Date: Tue, 7 Sep 2021 12:11:34 +0200 Subject: [PATCH 38/80] RUMM-1455 Rename `onSessionStart` to `onRUMSessionStart` --- Sources/Datadog/DatadogConfiguration.swift | 4 ++-- Sources/DatadogObjc/DatadogConfiguration+objc.swift | 4 ++-- .../Datadog/DatadogConfigurationBuilderTests.swift | 3 +++ .../DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index a303218a63..e2d596f682 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -507,13 +507,13 @@ extension Datadog { return self } - /// Sets the Session start callback. + /// Sets the RUM Session start callback. /// /// The callback takes 2 arguments: the newly started Session ID and a boolean indicating whether or not the session is discarded by the sampling rate /// (when `true` it means no event in this session will be kept). /// /// - Parameter handler: the callback handler to notify whenever a new Session starts. - public func onSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder { + public func onRUMSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder { configuration.rumSessionsListener = handler return self } diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index 44f845338e..d43e33a334 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -255,8 +255,8 @@ public class DDConfigurationBuilder: NSObject { } @objc - public func set(onSessionStart handler: @escaping (String, Bool) -> Void) { - _ = sdkBuilder.onSessionStart(handler) + public func set(onRUMSessionStart handler: @escaping (String, Bool) -> Void) { + _ = sdkBuilder.onRUMSessionStart(handler) } @objc diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index fbb3527d68..6754ced971 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -45,6 +45,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertNil(configuration.firstPartyHosts) XCTAssertNil(configuration.spanEventMapper) XCTAssertEqual(configuration.rumSessionsSamplingRate, 100.0) + XCTAssertNil(configuration.rumSessionsListener) XCTAssertNil(configuration.rumUIKitViewsPredicate) XCTAssertNil(configuration.rumUIKitUserActionsPredicate) XCTAssertNil(configuration.rumViewEventMapper) @@ -78,6 +79,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { .set(customTracesEndpoint: URL(string: "https://api.custom.traces/")!) .set(customRUMEndpoint: URL(string: "https://api.custom.rum/")!) .set(rumSessionsSamplingRate: 42.5) + .onRUMSessionStart { _, _ in } .setSpanEventMapper { _ in mockSpanEvent } .trackURLSession(firstPartyHosts: ["example.com"]) .trackUIKitRUMViews(using: UIKitRUMViewsPredicateMock()) @@ -133,6 +135,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertEqual(configuration.customRUMEndpoint, URL(string: "https://api.custom.rum/")!) XCTAssertEqual(configuration.firstPartyHosts, ["example.com"]) XCTAssertEqual(configuration.rumSessionsSamplingRate, 42.5) + XCTAssertNotNil(configuration.rumSessionsListener) XCTAssertTrue(configuration.rumUIKitViewsPredicate is UIKitRUMViewsPredicateMock) XCTAssertTrue(configuration.rumUIKitUserActionsPredicate is UIKitRUMUserActionsPredicateMock) XCTAssertEqual(configuration.spanEventMapper?(.mockRandom()), mockSpanEvent) diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m index 5b0d63dd20..e167a738b7 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m @@ -82,6 +82,7 @@ - (void)testDDConfigurationBuilderAPI { [builder trackURLSessionWithFirstPartyHosts:[NSSet setWithArray:@[]]]; [builder setWithServiceName:@""]; [builder setWithRumSessionsSamplingRate:50]; + [builder setOnRUMSessionStart:^(NSString * _Nonnull sessionId, BOOL isDiscarded) {}]; [builder trackUIKitRUMViews]; [builder trackUIKitRUMViewsUsing:[CustomDDUIKitRUMViewsPredicate new]]; [builder trackUIKitRUMActions]; From d7aca8e752cca3543bad3ed7bc8d35cc1c2574a2 Mon Sep 17 00:00:00 2001 From: maxep Date: Tue, 7 Sep 2021 12:12:28 +0200 Subject: [PATCH 39/80] RUMM-1455 Use fuzzy sample rate and expectation fulfillment --- .../Scopes/RUMApplicationScopeTests.swift | 37 ++++++++++++++++++- .../Datadog/RUMMonitorTests.swift | 17 ++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift index 73c80190fd..061033b61a 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift @@ -24,16 +24,48 @@ class RUMApplicationScopeTests: XCTestCase { } func testWhenFirstViewIsStarted_itStartsNewSession() { - let scope = RUMApplicationScope(rumApplicationID: .mockAny(), dependencies: .mockAny(), samplingRate: 100, backgroundEventTrackingEnabled: .mockAny()) + let expectation = expectation(description: "onSessionStart is called") + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertTrue(isDiscarded) + expectation.fulfill() + } + + let scope = RUMApplicationScope( + rumApplicationID: .mockAny(), + dependencies: .mockWith( + onSessionStart: onSessionStart + ), + samplingRate: 0, + backgroundEventTrackingEnabled: .mockAny() + ) XCTAssertNil(scope.sessionScope) XCTAssertTrue(scope.process(command: RUMStartViewCommand.mockAny())) + waitForExpectations(timeout: 0.5) XCTAssertNotNil(scope.sessionScope) XCTAssertEqual(scope.sessionScope?.backgroundEventTrackingEnabled, scope.backgroundEventTrackingEnabled) } func testWhenSessionExpires_itStartsANewOneAndTransfersActiveViews() throws { - let scope = RUMApplicationScope(rumApplicationID: .mockAny(), dependencies: .mockAny(), samplingRate: 100, backgroundEventTrackingEnabled: .mockAny()) + let expectation = expectation(description: "onSessionStart is called twice") + expectation.expectedFulfillmentCount = 2 + + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertFalse(isDiscarded) + expectation.fulfill() + } + + let scope = RUMApplicationScope( + rumApplicationID: .mockAny(), + dependencies: .mockWith( + onSessionStart: onSessionStart + ), + samplingRate: 100, + backgroundEventTrackingEnabled: .mockAny() + ) + var currentTime = Date() let view = createMockViewInWindow() @@ -49,6 +81,7 @@ class RUMApplicationScopeTests: XCTestCase { let secondSessionViewScopes = try XCTUnwrap(scope.sessionScope?.viewScopes) let secondSessionViewScope = try XCTUnwrap(secondSessionViewScopes.first) + waitForExpectations(timeout: 0.5) XCTAssertNotEqual(firstSessionUUID, secondSessionUUID) XCTAssertEqual(firstsSessionViewScopes.count, secondSessionViewScopes.count) XCTAssertTrue(secondSessionViewScope.identity.equals(view)) diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index efefa57a94..ef11fe0c00 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -788,14 +788,20 @@ class RUMMonitorTests: XCTestCase { // MARK: - RUM New Session func testStartingViewCreatesNewSession() { + let keepAllSessions: Bool = .random() + + let expectation = expectation(description: "onSessionStart is called") + let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in + XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) + XCTAssertEqual(isDiscarded, !keepAllSessions) + expectation.fulfill() + } + RUMFeature.instance = .mockWith( directories: temporaryFeatureDirectories, configuration: .mockWith( - sessionSamplingRate: 100, - onSessionStart: { sessionId, isDiscarded in - XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) - XCTAssertFalse(isDiscarded) - } + sessionSamplingRate: keepAllSessions ? 100 : 0, + onSessionStart: onSessionStart ) ) @@ -803,6 +809,7 @@ class RUMMonitorTests: XCTestCase { let monitor = RUMMonitor.initialize() monitor.startView(viewController: mockView) + waitForExpectations(timeout: 0.5) } // MARK: - RUM Events Dates Correction From a7ae11c3018961234317dfa16fefea61f194bf4b Mon Sep 17 00:00:00 2001 From: maxep Date: Tue, 7 Sep 2021 12:15:32 +0200 Subject: [PATCH 40/80] RUMM-1455 API surface --- api-surface-objc | 2 +- api-surface-swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api-surface-objc b/api-surface-objc index 994267c5c9..774afd99c3 100644 --- a/api-surface-objc +++ b/api-surface-objc @@ -69,7 +69,7 @@ public class DDConfigurationBuilder: NSObject public func trackURLSession(firstPartyHosts: Set) public func set(serviceName: String) public func set(rumSessionsSamplingRate: Float) - public func set(onSessionStart handler: @escaping (String, Bool) -> Void) + public func set(onRUMSessionStart handler: @escaping (String, Bool) -> Void) public func trackUIKitRUMViews() public func trackUIKitRUMViews(using predicate: DDUIKitRUMViewsPredicate) public func trackUIKitActions() diff --git a/api-surface-swift b/api-surface-swift index 0b843f30bf..66cf573903 100644 --- a/api-surface-swift +++ b/api-surface-swift @@ -147,7 +147,7 @@ public class Datadog public func enableRUM(_ enabled: Bool) -> Builder public func set(rumEndpoint: RUMEndpoint) -> Builder public func set(rumSessionsSamplingRate: Float) -> Builder - public func onSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder + public func onRUMSessionStart(_ handler: @escaping (String, Bool) -> Void) -> Builder public func trackUIKitRUMViews(using predicate: UIKitRUMViewsPredicate = DefaultUIKitRUMViewsPredicate()) -> Builder public func trackUIKitActions(_ enabled: Bool = true) -> Builder public func trackUIKitRUMActions(using predicate: UIKitRUMUserActionsPredicate = DefaultUIKitRUMUserActionsPredicate()) -> Builder From 4dc2ecc9a532b43131566f80198c3d04e5169e62 Mon Sep 17 00:00:00 2001 From: maxep Date: Tue, 7 Sep 2021 12:30:53 +0200 Subject: [PATCH 41/80] RUMM-1455 Fix test build --- .../RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift | 4 ++-- Tests/DatadogTests/Datadog/RUMMonitorTests.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift index 061033b61a..b73e906a30 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMApplicationScopeTests.swift @@ -24,7 +24,7 @@ class RUMApplicationScopeTests: XCTestCase { } func testWhenFirstViewIsStarted_itStartsNewSession() { - let expectation = expectation(description: "onSessionStart is called") + let expectation = self.expectation(description: "onSessionStart is called") let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) XCTAssertTrue(isDiscarded) @@ -48,7 +48,7 @@ class RUMApplicationScopeTests: XCTestCase { } func testWhenSessionExpires_itStartsANewOneAndTransfersActiveViews() throws { - let expectation = expectation(description: "onSessionStart is called twice") + let expectation = self.expectation(description: "onSessionStart is called twice") expectation.expectedFulfillmentCount = 2 let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index ef11fe0c00..8f7045fb3f 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -790,7 +790,7 @@ class RUMMonitorTests: XCTestCase { func testStartingViewCreatesNewSession() { let keepAllSessions: Bool = .random() - let expectation = expectation(description: "onSessionStart is called") + let expectation = self.expectation(description: "onSessionStart is called") let onSessionStart: RUMSessionListener = { sessionId, isDiscarded in XCTAssertTrue(sessionId.matches(regex: .uuidRegex)) XCTAssertEqual(isDiscarded, !keepAllSessions) From 61268edcdc1033ffaa6123b8ca95912d7c53b8d8 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Fri, 10 Sep 2021 17:52:39 +0200 Subject: [PATCH 42/80] RUMM-1373 Fix flakiness in `VitalMemoryReaderTests` --- .../RUMVitals/VitalMemoryReaderTests.swift | 94 +++++++++++++------ 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalMemoryReaderTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalMemoryReaderTests.swift index eb7e42623d..181a4ff3ad 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalMemoryReaderTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalMemoryReaderTests.swift @@ -7,58 +7,90 @@ import XCTest @testable import Datadog +private class Allocation { + fileprivate let numberOfBytes: Int + private var pointer: UnsafeMutablePointer! // swiftlint:disable:this implicitly_unwrapped_optional + + init(numberOfBytes: Int) { + self.numberOfBytes = numberOfBytes + } + + func allocate() { + pointer = UnsafeMutablePointer.allocate(capacity: numberOfBytes) + pointer.initialize(repeating: 0, count: numberOfBytes) + } + + func deallocate() { + pointer.deallocate() + } +} + internal class VitalMemoryReaderTest: XCTestCase { + private let allocation = Allocation( + numberOfBytes: 8 * 1_024 * 1_024 // 8MB is significantly more than any other allocation in tests process + ) + func testReadMemory() throws { let reader = VitalMemoryReader() - let result = reader.readVitalData() - XCTAssertNotNil(result) } - func testWhenMemoryConsumptionGrows() { + func testWhenMemoryConsumptionGrows() throws { // Given let reader = VitalMemoryReader() - let threshold = reader.readVitalData() - let allocationSize = 128 * 1_024 // When - let intPointer = UnsafeMutablePointer.allocate(capacity: allocationSize) - for i in 0...stride / 2 - XCTAssertNotNil(threshold) - XCTAssertNotNil(measure) - let delta = measure! - threshold! - XCTAssertGreaterThanOrEqual(delta, Double(expectedAllocatedSize)) + let meanDelta = deltas.reduce(0.0, +) / Double(deltas.count) + let expectedMeanDelta = Double(allocation.numberOfBytes) * 0.6 // only 60% to mitigate external deallocations in the process + + XCTAssertGreaterThan( + meanDelta, + expectedMeanDelta, + "Mean delta \(toMB(meanDelta))MB is not greater than \(toMB(expectedMeanDelta))MB" + ) } - func testWhenMemoryConsumptionShrinks() { + func testWhenMemoryConsumptionShrinks() throws { // Given let reader = VitalMemoryReader() - let allocationSize = 128 * 1_024 - let intPointer = UnsafeMutablePointer.allocate(capacity: allocationSize) - for i in 0...stride / 2 - XCTAssertNotNil(threshold) - XCTAssertNotNil(measure) - let delta = threshold! - measure! - XCTAssertGreaterThanOrEqual(delta, Double(expectedAllocatedSize)) + let meanDelta = deltas.reduce(0.0, +) / Double(deltas.count) + let expectedMeanDelta = Double(allocation.numberOfBytes) * 0.6 // only 60% to mitigate external allocations in the process + + XCTAssertLessThan( + meanDelta, + expectedMeanDelta, + "Mean delta \(toMB(meanDelta))MB is not less than \(toMB(expectedMeanDelta))MB" + ) + } + + // MARK: - Helpers + + private func toMB(_ bytes: Double) -> Double { + return round(bytes / (1_024 * 1_024) * 100) / 100 } } From 511c7a96f2e8bbac3759e477fb78b934d26b71a6 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Mon, 13 Sep 2021 18:48:39 +0200 Subject: [PATCH 43/80] RUMM-1373 Fix flakiness in URLSession- and URLSessionDelegate-based tests by temporary disabling network instrumentation in `dd-sdk-swift-testing`. It should be enabled back, once the issue is fixed in `dd-sdk-swift-testing`. --- .../xcshareddata/xcschemes/Datadog.xcscheme | 5 +++++ tools/nightly-unit-tests/bitrise.yml.src | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Datadog.xcscheme b/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Datadog.xcscheme index 859579c932..50cc60bb65 100644 --- a/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Datadog.xcscheme +++ b/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Datadog.xcscheme @@ -76,6 +76,11 @@ value = "1" isEnabled = "YES"> + + > xcconfigs/DatadogSDKTesting.local.xcconfig + _run_unit_tests: steps: - script@1.1.6: From 22b2c7f2339f0063086eb79af1d619bda493c7bd Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 12:19:25 +0200 Subject: [PATCH 44/80] RUMM-1373 Fix flakines caused by altering global state This includes adding unit tests observer to perform common integrity checks after each test. --- Datadog/Datadog.xcodeproj/project.pbxproj | 16 ++ .../Core/Swizzling/MethodSwizzler.swift | 15 ++ .../RUMIntegrationsTests.swift | 4 +- Tests/DatadogTests/Datadog/GlobalTests.swift | 4 + .../Datadog/LoggerBuilderTests.swift | 3 + .../Scopes/RUMSessionScopeTests.swift | 16 ++ .../RUMMonitor/Scopes/RUMViewScopeTests.swift | 9 + .../RUMMonitorConfigurationTests.swift | 3 + .../Datadog/TracerConfigurationTests.swift | 3 + .../DatadogTests/Helpers/TestsDirectory.swift | 5 + .../TestsObserver/DatadogTestsObserver.swift | 164 ++++++++++++++++++ .../DatadogTestsObserverLoader.m | 20 +++ 12 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift create mode 100644 Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 52d9228c4d..67609f2820 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -266,6 +266,8 @@ 617CD0DD24CEDDD300B0B557 /* RUMUserActionScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617CD0DC24CEDDD300B0B557 /* RUMUserActionScopeTests.swift */; }; 617CEB392456BC3A00AD4669 /* TracingUUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617CEB382456BC3A00AD4669 /* TracingUUID.swift */; }; 6182374325D3DFD5006A375B /* CrashReportingWithRUMIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6182374225D3DFD5006A375B /* CrashReportingWithRUMIntegrationTests.swift */; }; + 6184751526EFCF1300C7C9C5 /* DatadogTestsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6184751426EFCF1300C7C9C5 /* DatadogTestsObserver.swift */; }; + 6184751826EFD03400C7C9C5 /* DatadogTestsObserverLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 6184751726EFD03400C7C9C5 /* DatadogTestsObserverLoader.m */; }; 618715F724DC0CDE00FC0F69 /* RUMCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618715F624DC0CDE00FC0F69 /* RUMCommandTests.swift */; }; 618715F924DC13A100FC0F69 /* RUMDataModelsMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618715F824DC13A100FC0F69 /* RUMDataModelsMapping.swift */; }; 618715FC24DC5F0800FC0F69 /* RUMDataModelsMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618715FB24DC5F0800FC0F69 /* RUMDataModelsMappingTests.swift */; }; @@ -898,6 +900,8 @@ 617CD0DC24CEDDD300B0B557 /* RUMUserActionScopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMUserActionScopeTests.swift; sourceTree = ""; }; 617CEB382456BC3A00AD4669 /* TracingUUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingUUID.swift; sourceTree = ""; }; 6182374225D3DFD5006A375B /* CrashReportingWithRUMIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportingWithRUMIntegrationTests.swift; sourceTree = ""; }; + 6184751426EFCF1300C7C9C5 /* DatadogTestsObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogTestsObserver.swift; sourceTree = ""; }; + 6184751726EFD03400C7C9C5 /* DatadogTestsObserverLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DatadogTestsObserverLoader.m; sourceTree = ""; }; 6185EB0F25FA94A700B43E2E /* Base.local.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.local.xcconfig; sourceTree = ""; }; 618715F624DC0CDE00FC0F69 /* RUMCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMCommandTests.swift; sourceTree = ""; }; 618715F824DC13A100FC0F69 /* RUMDataModelsMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMDataModelsMapping.swift; sourceTree = ""; }; @@ -1558,6 +1562,7 @@ 61133C122423990D00786299 /* DatadogTests */ = { isa = PBXGroup; children = ( + 6184751626EFD01600C7C9C5 /* TestsObserver */, 61133C182423990D00786299 /* Datadog */, 61133C132423990D00786299 /* DatadogObjc */, 61C3637E2436163400C4D4E6 /* DatadogPrivate */, @@ -2429,6 +2434,15 @@ path = CrashReporting; sourceTree = ""; }; + 6184751626EFD01600C7C9C5 /* TestsObserver */ = { + isa = PBXGroup; + children = ( + 6184751426EFCF1300C7C9C5 /* DatadogTestsObserver.swift */, + 6184751726EFD03400C7C9C5 /* DatadogTestsObserverLoader.m */, + ); + path = TestsObserver; + sourceTree = ""; + }; 618715FA24DC5EE700FC0F69 /* DataModels */ = { isa = PBXGroup; children = ( @@ -3955,6 +3969,7 @@ 613F23F1252B129E006CD2D7 /* URLSessionRUMResourcesHandlerTests.swift in Sources */, 61B03879252724AB00518F3C /* URLSessionInterceptorTests.swift in Sources */, 61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */, + 6184751526EFCF1300C7C9C5 /* DatadogTestsObserver.swift in Sources */, 61133C602423990D00786299 /* RequestBuilderTests.swift in Sources */, 61133C572423990D00786299 /* FileReaderTests.swift in Sources */, D2F1B81326D8DA68009F3293 /* DDNoopRUMMonitorTests.swift in Sources */, @@ -4055,6 +4070,7 @@ 61133C532423990D00786299 /* MobileDeviceTests.swift in Sources */, 61FF282124B8981D000B3D9B /* RUMEventBuilderTests.swift in Sources */, 61B5E42926DFB60A000B0A5F /* DDConfiguration+apiTests.m in Sources */, + 6184751826EFD03400C7C9C5 /* DatadogTestsObserverLoader.m in Sources */, 61345613244756E300E7DA6B /* PerformancePresetTests.swift in Sources */, 616A9CD22535D38200DB83CF /* UIKitHierarchyInspectorTests.swift in Sources */, ); diff --git a/Sources/Datadog/Core/Swizzling/MethodSwizzler.swift b/Sources/Datadog/Core/Swizzling/MethodSwizzler.swift index 7e78f3927e..0a9df6d088 100644 --- a/Sources/Datadog/Core/Swizzling/MethodSwizzler.swift +++ b/Sources/Datadog/Core/Swizzling/MethodSwizzler.swift @@ -66,6 +66,10 @@ internal class MethodSwizzler { let newImp: IMP = imp_implementationWithBlock(newImpBlock) set(newIMP: newImp, for: foundMethod) + + #if DD_SDK_COMPILED_FOR_TESTING + activeSwizzlingNames.append(foundMethod.swizzlingName) + #endif } } @@ -76,6 +80,8 @@ internal class MethodSwizzler { let originalTypedIMP = originalImplementation(of: foundMethod) let originalIMP: IMP = unsafeBitCast(originalTypedIMP, to: IMP.self) method_setImplementation(foundMethod.method, originalIMP) + + activeSwizzlingNames.removeAll { $0 == foundMethod.swizzlingName } } } #endif @@ -114,3 +120,12 @@ internal class MethodSwizzler { method_setImplementation(found.method, newIMP) } } + +#if DD_SDK_COMPILED_FOR_TESTING +extension MethodSwizzler.FoundMethod { + var swizzlingName: String { "\(klass).\(method_getName(method))" } +} + +/// The list of active swizzlings to ensure integrity in unit tests. +internal var activeSwizzlingNames: [String] = [] +#endif diff --git a/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMIntegrationsTests.swift b/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMIntegrationsTests.swift index 4e902b7e7a..1dd767cc51 100644 --- a/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMIntegrationsTests.swift +++ b/Tests/DatadogTests/Datadog/FeaturesIntegration/RUMIntegrationsTests.swift @@ -74,12 +74,12 @@ class RUMIntegrationsTests: XCTestCase { class RUMErrorsIntegrationTests: XCTestCase { private let integration = RUMErrorsIntegration() - override class func setUp() { + override func setUp() { super.setUp() temporaryFeatureDirectories.create() } - override class func tearDown() { + override func tearDown() { super.tearDown() temporaryFeatureDirectories.delete() } diff --git a/Tests/DatadogTests/Datadog/GlobalTests.swift b/Tests/DatadogTests/Datadog/GlobalTests.swift index 2717f288fd..5bf12f5448 100644 --- a/Tests/DatadogTests/Datadog/GlobalTests.swift +++ b/Tests/DatadogTests/Datadog/GlobalTests.swift @@ -16,6 +16,10 @@ class GlobalTests: XCTestCase { XCTAssertTrue(Global.rum is DDNoopRUMMonitor) } + func testWhenCrashReportingIsNotEnabled_thenCrashReporterIsNotSet() { + XCTAssertNil(Global.crashReporter) + } + func testDDGlobalIsGlobalTypealias() { XCTAssertTrue(DDGlobal.self == Global.self) } diff --git a/Tests/DatadogTests/Datadog/LoggerBuilderTests.swift b/Tests/DatadogTests/Datadog/LoggerBuilderTests.swift index 7bf7154a5b..ced6e20706 100644 --- a/Tests/DatadogTests/Datadog/LoggerBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/LoggerBuilderTests.swift @@ -13,6 +13,8 @@ class LoggerBuilderTests: XCTestCase { override func setUp() { super.setUp() + temporaryFeatureDirectories.create() + LoggingFeature.instance = .mockByRecordingLogMatchers( directories: temporaryFeatureDirectories, configuration: .mockWith( @@ -32,6 +34,7 @@ class LoggerBuilderTests: XCTestCase { override func tearDown() { LoggingFeature.instance?.deinitialize() + temporaryFeatureDirectories.delete() super.tearDown() } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScopeTests.swift index d243c96ad6..0659bb918f 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMSessionScopeTests.swift @@ -85,8 +85,12 @@ class RUMSessionScopeTests: XCTestCase { let scope = RUMSessionScope(parent: parent, dependencies: .mockAny(), samplingRate: 100, startTime: Date(), backgroundEventTrackingEnabled: true) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) + _ = scope.process(command: RUMStartResourceCommand.mockWith(resourceKey: "/resource/1", time: currentTime)) XCTAssertEqual(scope.viewScopes.count, 1) @@ -102,8 +106,12 @@ class RUMSessionScopeTests: XCTestCase { let scope = RUMSessionScope(parent: parent, dependencies: .mockAny(), samplingRate: 100, startTime: Date(), backgroundEventTrackingEnabled: true) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) + _ = scope.process(command: RUMStartUserActionCommand.mockWith(time: currentTime)) XCTAssertEqual(scope.viewScopes.count, 1) @@ -119,8 +127,12 @@ class RUMSessionScopeTests: XCTestCase { let scope = RUMSessionScope(parent: parent, dependencies: .mockAny(), samplingRate: 100, startTime: Date(), backgroundEventTrackingEnabled: true) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) + _ = scope.process(command: RUMAddUserActionCommand.mockWith(time: currentTime)) XCTAssertEqual(scope.viewScopes.count, 1) @@ -200,8 +212,12 @@ class RUMSessionScopeTests: XCTestCase { let scope = RUMSessionScope(parent: parent, dependencies: .mockAny(), samplingRate: 100, startTime: Date(), backgroundEventTrackingEnabled: .mockAny()) XCTAssertEqual(scope.viewScopes.count, 0) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) + let command = generateRandomNotValidStartCommand() // When diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index fc38d38c21..4a6f99ede3 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -436,6 +436,9 @@ class RUMViewScopeTests: XCTestCase { startTime: Date() ) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) @@ -493,6 +496,9 @@ class RUMViewScopeTests: XCTestCase { startTime: currentTime ) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) @@ -732,6 +738,9 @@ class RUMViewScopeTests: XCTestCase { XCTAssertTrue(scope.isActiveView) XCTAssertEqual(scope.customTimings.count, 0) + let previousUserLogger = userLogger + defer { userLogger = previousUserLogger } + let logOutput = LogOutputMock() userLogger = .mockWith(logOutput: logOutput) diff --git a/Tests/DatadogTests/Datadog/RUMMonitorConfigurationTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorConfigurationTests.swift index 47e75752bf..416e2596f8 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorConfigurationTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorConfigurationTests.swift @@ -12,6 +12,9 @@ class RUMMonitorConfigurationTests: XCTestCase { private let carrierInfoProvider: CarrierInfoProviderMock = .mockAny() func testRUMMonitorConfiguration() throws { + temporaryFeatureDirectories.create() + defer { temporaryFeatureDirectories.delete() } + RUMFeature.instance = .mockByRecordingRUMEventMatchers( directories: temporaryFeatureDirectories, configuration: .mockWith( diff --git a/Tests/DatadogTests/Datadog/TracerConfigurationTests.swift b/Tests/DatadogTests/Datadog/TracerConfigurationTests.swift index e849db85d9..64079c67cb 100644 --- a/Tests/DatadogTests/Datadog/TracerConfigurationTests.swift +++ b/Tests/DatadogTests/Datadog/TracerConfigurationTests.swift @@ -13,6 +13,8 @@ class TracerConfigurationTests: XCTestCase { override func setUp() { super.setUp() + temporaryFeatureDirectories.create() + TracingFeature.instance = .mockByRecordingSpanMatchers( directories: temporaryFeatureDirectories, configuration: .mockWith( @@ -32,6 +34,7 @@ class TracerConfigurationTests: XCTestCase { override func tearDown() { TracingFeature.instance?.deinitialize() + temporaryFeatureDirectories.delete() super.tearDown() } diff --git a/Tests/DatadogTests/Helpers/TestsDirectory.swift b/Tests/DatadogTests/Helpers/TestsDirectory.swift index 1b1a83072b..dde01969e7 100644 --- a/Tests/DatadogTests/Helpers/TestsDirectory.swift +++ b/Tests/DatadogTests/Helpers/TestsDirectory.swift @@ -46,6 +46,11 @@ extension Directory { } } + /// Checks if directory exists. + func exists() -> Bool { + return FileManager.default.fileExists(atPath: url.path) + } + func createMockFiles(count: Int, prefix: String = "file") { (0..(...) // <-- after return, no reference to `server` will exist as it processed all callbacks and got be safely deallocated + ``` + """ + ), + .init( + assert: { + temporaryFeatureDirectories.authorized.exists() == false + && temporaryFeatureDirectories.unauthorized.exists() == false + }, + problem: "`temporaryFeatureDirectories` must not exist.", + solution: """ + Make sure that `temporaryFeatureDirectories` is unifromly managed in every test by using: + ``` + // Before test: + temporaryFeatureDirectories.create() + + // After test: + temporaryFeatureDirectories.delete() + ``` + """ + ) + ] + + func testCaseDidFinish(_ testCase: XCTestCase) { + performIntegrityChecks(after: testCase) + } + + private func performIntegrityChecks(after testCase: XCTestCase) { + let failedChecks = checks.filter { $0.assert() == false } + + if !failedChecks.isEmpty { + var message = """ + 🐶✋ `DatadogTests` integrity check failure. + + `DatadogTestsObserver` found that `\(testCase.name)` breaks \(failedChecks.count) integrity rule(s) which + must be fulfilled before and after each unit test: + """ + failedChecks.forEach { check in + message += """ + \n⚠️ ---- \(check.problem) ---- + 🔎 \(check.solution()) + """ + } + + message += "\n" + preconditionFailure(message) + } + } +} + +private struct TestIntegrityCheck { + /// If this assertion evaluates to `false`, the integrity issue is raised. + let assert: () -> Bool + /// What is the assertion about? + let problem: StaticString + /// How to fix it if it fails? + let solution: () -> String + + init(assert: @escaping () -> Bool, problem: StaticString, solution: @escaping @autoclosure () -> String) { + self.assert = assert + self.problem = problem + self.solution = solution + } +} diff --git a/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m b/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m new file mode 100644 index 0000000000..08093ef45b --- /dev/null +++ b/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m @@ -0,0 +1,20 @@ +/* + * 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. + */ + +#import +#import + +/// This code runs when the `DatadogTests` bundle is loaded into memory and tests start. +/// Reference: https://developer.apple.com/documentation/objectivec/nsobject/1418815-load +__attribute__((constructor)) static void initialize_FrameworkLoadHandler() { + Class testsObserverClass = objc_getClass("DatadogTests.DatadogTestsObserver"); + SEL startObservingSelector = NSSelectorFromString(@"startObserving"); + NSMethodSignature *methodSignature = [testsObserverClass methodSignatureForSelector:startObservingSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + [invocation setTarget:testsObserverClass]; + [invocation setSelector:startObservingSelector]; + [invocation invoke]; +} From c989ffd632751d6e180629b0b551b646e99c467e Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 12:22:43 +0200 Subject: [PATCH 45/80] RUMM-1373 Fix `[NSError init] called` runtime error Full error context found in runtime logs: -[NSError init] called; this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once. --- .../DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m index 20e297d6c8..9c1fa93e75 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m @@ -56,14 +56,16 @@ - (void)testDDRUMMonitorAPI { [monitor startViewWithKey:@"" name:nil attributes:@{}]; [monitor stopViewWithKey:@"" attributes:@{}]; [monitor addErrorWithMessage:@"" source:DDRUMErrorSourceCustom stack:nil attributes:@{}]; - [monitor addErrorWithError:[NSError new] source:DDRUMErrorSourceNetwork attributes:@{}]; + [monitor addErrorWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:-100 userInfo:nil] + source:DDRUMErrorSourceNetwork attributes:@{}]; [monitor startResourceLoadingWithResourceKey:@"" request:[NSURLRequest new] attributes:@{}]; [monitor startResourceLoadingWithResourceKey:@"" url:[NSURL new] attributes:@{}]; [monitor startResourceLoadingWithResourceKey:@"" httpMethod:DDRUMMethodGet urlString:@"" attributes:@{}]; [monitor addResourceMetricsWithResourceKey:@"" metrics:[NSURLSessionTaskMetrics new] attributes:@{}]; [monitor stopResourceLoadingWithResourceKey:@"" response:[NSURLResponse new] size:nil attributes:@{}]; [monitor stopResourceLoadingWithResourceKey:@"" statusCode:nil kind:DDRUMResourceTypeOther size:nil attributes:@{}]; - [monitor stopResourceLoadingWithErrorWithResourceKey:@"" error:[NSError new] response:nil attributes:@{}]; + [monitor stopResourceLoadingWithErrorWithResourceKey:@"" + error:[NSError errorWithDomain:NSURLErrorDomain code:-99 userInfo:nil] response:nil attributes:@{}]; [monitor stopResourceLoadingWithErrorWithResourceKey:@"" errorMessage:@"" response:nil attributes:@{}]; [monitor startUserActionWithType:DDRUMUserActionTypeSwipe name:@"" attributes:@{}]; [monitor stopUserActionWithType:DDRUMUserActionTypeSwipe name:nil attributes:@{}]; From 92da450dc04d8d1cbf35d1c0c74004489152bffc Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 13:27:01 +0200 Subject: [PATCH 46/80] RUMM-1373 Fix tests flakiness in `DataUploadWorkerTests` and cleanup unused `defaultUploadDelay` in `PerformancePreset`. --- Sources/Datadog/Core/PerformancePreset.swift | 11 +++-------- Sources/Datadog/Core/Upload/DataUploadDelay.swift | 3 --- .../Datadog/Core/PerformancePresetTests.swift | 6 ------ .../Datadog/Core/Upload/DataUploadDelayTests.swift | 1 - .../Datadog/Core/Upload/DataUploadWorkerTests.swift | 8 +++++--- .../Datadog/Logging/LoggingFeatureTests.swift | 1 - Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift | 13 +++++++++---- Tests/DatadogTests/Datadog/Mocks/ServerMock.swift | 2 +- .../DatadogTests/Datadog/RUM/RUMFeatureTests.swift | 1 - .../Datadog/Tracing/TracingFeatureTests.swift | 1 - 10 files changed, 18 insertions(+), 29 deletions(-) diff --git a/Sources/Datadog/Core/PerformancePreset.swift b/Sources/Datadog/Core/PerformancePreset.swift index 2c122ce7b8..efc6274b25 100644 --- a/Sources/Datadog/Core/PerformancePreset.swift +++ b/Sources/Datadog/Core/PerformancePreset.swift @@ -34,13 +34,10 @@ internal protocol StoragePerformancePreset { } internal protocol UploadPerformancePreset { - /// First upload delay (in seconds). - /// It is used as a base value until no more files eligible for upload are found - then `defaultUploadDelay` is used as a new base. - var initialUploadDelay: TimeInterval { get } - /// Default uploads interval (in seconds). - /// At runtime, the upload interval ranges from `minUploadDelay` to `maxUploadDelay` depending + /// Initial upload delay (in seconds). + /// At runtime, the upload interval starts with `initialUploadDelay` and then ranges from `minUploadDelay` to `maxUploadDelay` depending /// on delivery success or failure. - var defaultUploadDelay: TimeInterval { get } + var initialUploadDelay: TimeInterval { get } /// Mininum interval of data upload (in seconds). var minUploadDelay: TimeInterval { get } /// Maximum interval of data upload (in seconds). @@ -64,7 +61,6 @@ internal struct PerformancePreset: Equatable, StoragePerformancePreset, UploadPe // MARK: - UploadPerformancePreset let initialUploadDelay: TimeInterval - let defaultUploadDelay: TimeInterval let minUploadDelay: TimeInterval let maxUploadDelay: TimeInterval let uploadDelayChangeRate: Double @@ -139,7 +135,6 @@ internal extension PerformancePreset { self.maxObjectsInFile = 500 self.maxObjectSize = 256 * 1_024 // 256KB self.initialUploadDelay = minUploadDelay * uploadDelayFactors.initial - self.defaultUploadDelay = minUploadDelay * uploadDelayFactors.default self.minUploadDelay = minUploadDelay * uploadDelayFactors.min self.maxUploadDelay = minUploadDelay * uploadDelayFactors.max self.uploadDelayChangeRate = uploadDelayFactors.changeRate diff --git a/Sources/Datadog/Core/Upload/DataUploadDelay.swift b/Sources/Datadog/Core/Upload/DataUploadDelay.swift index 8797c08bf6..66d794e910 100644 --- a/Sources/Datadog/Core/Upload/DataUploadDelay.swift +++ b/Sources/Datadog/Core/Upload/DataUploadDelay.swift @@ -14,15 +14,12 @@ internal protocol Delay { /// Mutable interval used for periodic data uploads. internal struct DataUploadDelay: Delay { - private let defaultDelay: TimeInterval private let minDelay: TimeInterval private let maxDelay: TimeInterval private let changeRate: Double - private var delay: TimeInterval init(performance: UploadPerformancePreset) { - self.defaultDelay = performance.defaultUploadDelay self.minDelay = performance.minUploadDelay self.maxDelay = performance.maxUploadDelay self.changeRate = performance.uploadDelayChangeRate diff --git a/Tests/DatadogTests/Datadog/Core/PerformancePresetTests.swift b/Tests/DatadogTests/Datadog/Core/PerformancePresetTests.swift index 3b518a2330..5cdac09734 100644 --- a/Tests/DatadogTests/Datadog/Core/PerformancePresetTests.swift +++ b/Tests/DatadogTests/Datadog/Core/PerformancePresetTests.swift @@ -26,7 +26,6 @@ class PerformancePresetTests: XCTestCase { let frequentUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .frequent, bundleType: .iOSApp) XCTAssertEqual(frequentUploadAnyBatch.initialUploadDelay, 5.0) - XCTAssertEqual(frequentUploadAnyBatch.defaultUploadDelay, 5.0) XCTAssertEqual(frequentUploadAnyBatch.minUploadDelay, 1.0) XCTAssertEqual(frequentUploadAnyBatch.maxUploadDelay, 10.0) XCTAssertEqual(frequentUploadAnyBatch.uploadDelayChangeRate, 0.1) @@ -34,7 +33,6 @@ class PerformancePresetTests: XCTestCase { let averageUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .average, bundleType: .iOSApp) XCTAssertEqual(averageUploadAnyBatch.initialUploadDelay, 25.0) - XCTAssertEqual(averageUploadAnyBatch.defaultUploadDelay, 25.0) XCTAssertEqual(averageUploadAnyBatch.minUploadDelay, 5.0) XCTAssertEqual(averageUploadAnyBatch.maxUploadDelay, 50.0) XCTAssertEqual(averageUploadAnyBatch.uploadDelayChangeRate, 0.1) @@ -42,7 +40,6 @@ class PerformancePresetTests: XCTestCase { let rareUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .rare, bundleType: .iOSApp) XCTAssertEqual(rareUploadAnyBatch.initialUploadDelay, 50.0) - XCTAssertEqual(rareUploadAnyBatch.defaultUploadDelay, 50.0) XCTAssertEqual(rareUploadAnyBatch.minUploadDelay, 10.0) XCTAssertEqual(rareUploadAnyBatch.maxUploadDelay, 100.0) XCTAssertEqual(rareUploadAnyBatch.uploadDelayChangeRate, 0.1) @@ -67,7 +64,6 @@ class PerformancePresetTests: XCTestCase { let frequentUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .frequent, bundleType: .iOSAppExtension) XCTAssertEqual(frequentUploadAnyBatch.initialUploadDelay, 0.25) - XCTAssertEqual(frequentUploadAnyBatch.defaultUploadDelay, 1.5) XCTAssertEqual(frequentUploadAnyBatch.minUploadDelay, 0.5) XCTAssertEqual(frequentUploadAnyBatch.maxUploadDelay, 2.5) XCTAssertEqual(frequentUploadAnyBatch.uploadDelayChangeRate, 0.5) @@ -75,7 +71,6 @@ class PerformancePresetTests: XCTestCase { let averageUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .average, bundleType: .iOSAppExtension) XCTAssertEqual(averageUploadAnyBatch.initialUploadDelay, 0.5) - XCTAssertEqual(averageUploadAnyBatch.defaultUploadDelay, 3.0) XCTAssertEqual(averageUploadAnyBatch.minUploadDelay, 1.0) XCTAssertEqual(averageUploadAnyBatch.maxUploadDelay, 5.0) XCTAssertEqual(averageUploadAnyBatch.uploadDelayChangeRate, 0.5) @@ -83,7 +78,6 @@ class PerformancePresetTests: XCTestCase { let rareUploadAnyBatch = PerformancePreset(batchSize: .mockRandom(), uploadFrequency: .rare, bundleType: .iOSAppExtension) XCTAssertEqual(rareUploadAnyBatch.initialUploadDelay, 2.5) - XCTAssertEqual(rareUploadAnyBatch.defaultUploadDelay, 15.0) XCTAssertEqual(rareUploadAnyBatch.minUploadDelay, 5.0) XCTAssertEqual(rareUploadAnyBatch.maxUploadDelay, 25.0) XCTAssertEqual(rareUploadAnyBatch.uploadDelayChangeRate, 0.5) diff --git a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadDelayTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadDelayTests.swift index 2f853dfb0f..b33b90325c 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadDelayTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadDelayTests.swift @@ -10,7 +10,6 @@ import XCTest class DataUploadDelayTests: XCTestCase { private let mockPerformance = UploadPerformanceMock( initialUploadDelay: 3, - defaultUploadDelay: 5, minUploadDelay: 1, maxUploadDelay: 20, uploadDelayChangeRate: 0.1 diff --git a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift index 26f96cc3e9..37493cbb4b 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift @@ -86,7 +86,7 @@ class DataUploadWorkerTests: XCTestCase { fileReader: reader, dataUploader: mockDataUploader, uploadConditions: .alwaysUpload(), - delay: DataUploadDelay(performance: UploadPerformanceMock.veryQuick), + delay: DataUploadDelay(performance: UploadPerformanceMock.veryQuickInitialUpload), featureName: .mockAny() ) @@ -101,7 +101,9 @@ class DataUploadWorkerTests: XCTestCase { let startUploadExpectation = self.expectation(description: "Upload has started") var mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: true)) - mockDataUploader.onUpload = { startUploadExpectation.fulfill() } + mockDataUploader.onUpload = { + startUploadExpectation.fulfill() + } // Given writer.write(value: ["key": "value"]) @@ -113,7 +115,7 @@ class DataUploadWorkerTests: XCTestCase { fileReader: reader, dataUploader: mockDataUploader, uploadConditions: .alwaysUpload(), - delay: DataUploadDelay(performance: UploadPerformanceMock.veryQuick), + delay: DataUploadDelay(performance: UploadPerformanceMock.veryQuickInitialUpload), featureName: .mockAny() ) diff --git a/Tests/DatadogTests/Datadog/Logging/LoggingFeatureTests.swift b/Tests/DatadogTests/Datadog/Logging/LoggingFeatureTests.swift index 46957f18c0..8dfb007cc8 100644 --- a/Tests/DatadogTests/Datadog/Logging/LoggingFeatureTests.swift +++ b/Tests/DatadogTests/Datadog/Logging/LoggingFeatureTests.swift @@ -96,7 +96,6 @@ class LoggingFeatureTests: XCTestCase { ), uploadPerformance: UploadPerformanceMock( initialUploadDelay: 0.5, // wait enough until spans are written, - defaultUploadDelay: 1, minUploadDelay: 1, maxUploadDelay: 1, uploadDelayChangeRate: 0 diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index 513bc6168e..26ef672069 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -373,26 +373,32 @@ struct StoragePerformanceMock: StoragePerformancePreset { struct UploadPerformanceMock: UploadPerformancePreset { let initialUploadDelay: TimeInterval - let defaultUploadDelay: TimeInterval let minUploadDelay: TimeInterval let maxUploadDelay: TimeInterval let uploadDelayChangeRate: Double static let noOp = UploadPerformanceMock( initialUploadDelay: .distantFuture, - defaultUploadDelay: .distantFuture, minUploadDelay: .distantFuture, maxUploadDelay: .distantFuture, uploadDelayChangeRate: 0 ) + /// Optimized for performing very fast uploads in unit tests. static let veryQuick = UploadPerformanceMock( initialUploadDelay: 0.05, - defaultUploadDelay: 0.05, minUploadDelay: 0.05, maxUploadDelay: 0.05, uploadDelayChangeRate: 0 ) + + /// Optimized for performing very fast first upload and then changing to unrealistically long intervals. + static let veryQuickInitialUpload = UploadPerformanceMock( + initialUploadDelay: 0.05, + minUploadDelay: 60, + maxUploadDelay: 60, + uploadDelayChangeRate: 60 / 0.05 + ) } extension PerformancePreset { @@ -406,7 +412,6 @@ extension PerformancePreset { maxObjectsInFile: storage.maxObjectsInFile, maxObjectSize: storage.maxObjectSize, initialUploadDelay: upload.initialUploadDelay, - defaultUploadDelay: upload.defaultUploadDelay, minUploadDelay: upload.minUploadDelay, maxUploadDelay: upload.maxUploadDelay, uploadDelayChangeRate: upload.uploadDelayChangeRate diff --git a/Tests/DatadogTests/Datadog/Mocks/ServerMock.swift b/Tests/DatadogTests/Datadog/Mocks/ServerMock.swift index 9e8b02813a..4774787a62 100644 --- a/Tests/DatadogTests/Datadog/Mocks/ServerMock.swift +++ b/Tests/DatadogTests/Datadog/Mocks/ServerMock.swift @@ -231,6 +231,6 @@ class ServerMock { let uploadPerformanceForTests = UploadPerformanceMock.veryQuick // Set the timeout to 40 times more than expected. // In `RUMM-311` we observed 0.66% of flakiness for 150 test runs on CI with arbitrary value of `20`. - return uploadPerformanceForTests.defaultUploadDelay * Double(numberOfRequestsMade) * 40 + return uploadPerformanceForTests.initialUploadDelay * Double(numberOfRequestsMade) * 40 } } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMFeatureTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMFeatureTests.swift index fbdba918e5..57490c08ce 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMFeatureTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMFeatureTests.swift @@ -105,7 +105,6 @@ class RUMFeatureTests: XCTestCase { ), uploadPerformance: UploadPerformanceMock( initialUploadDelay: 0.5, // wait enough until spans are written, - defaultUploadDelay: 1, minUploadDelay: 1, maxUploadDelay: 1, uploadDelayChangeRate: 0 diff --git a/Tests/DatadogTests/Datadog/Tracing/TracingFeatureTests.swift b/Tests/DatadogTests/Datadog/Tracing/TracingFeatureTests.swift index 1bc2063032..9317008704 100644 --- a/Tests/DatadogTests/Datadog/Tracing/TracingFeatureTests.swift +++ b/Tests/DatadogTests/Datadog/Tracing/TracingFeatureTests.swift @@ -97,7 +97,6 @@ class TracingFeatureTests: XCTestCase { ), uploadPerformance: UploadPerformanceMock( initialUploadDelay: 0.5, // wait enough until spans are written, - defaultUploadDelay: 1, minUploadDelay: 1, maxUploadDelay: 1, uploadDelayChangeRate: 0 From fd626f7330b43cee9f31340f866feef499e73f70 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 14:17:28 +0200 Subject: [PATCH 47/80] RUMM-1373 Fix flakiness in `RUMMonitorTests.testStartingViewCreatesNewSession()` because the test was using `RUMFeature` partial mock without `ServerMock`, the request that was sent from that test was hitting the `AssertedHTTPClient` causing expected crash. The flakiness was based on test execution - if test executed fast, then the request was sent after its completion. --- Tests/DatadogTests/Datadog/RUMMonitorTests.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index 7b4a43f522..325e2b6f96 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -801,6 +801,7 @@ class RUMMonitorTests: XCTestCase { expectation.fulfill() } + let server = ServerMock(delivery: .success(response: .mockResponseWith(statusCode: 200))) RUMFeature.instance = .mockWith( directories: temporaryFeatureDirectories, configuration: .mockWith( @@ -808,12 +809,14 @@ class RUMMonitorTests: XCTestCase { onSessionStart: onSessionStart ) ) - defer { RUMFeature.instance?.deinitialize() } let monitor = RUMMonitor.initialize() monitor.startView(viewController: mockView) + waitForExpectations(timeout: 0.5) + + _ = server.waitAndReturnRequests(count: keepAllSessions ? 1 : 0) } // MARK: - RUM Events Dates Correction From 889ac20a581f5c9c659c31c105b042b748246280 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 15:11:36 +0200 Subject: [PATCH 48/80] RUMM-1373 Cleanup --- .../Datadog/Core/Upload/DataUploadWorkerTests.swift | 4 +--- Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift index 37493cbb4b..815e013e73 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/DataUploadWorkerTests.swift @@ -101,9 +101,7 @@ class DataUploadWorkerTests: XCTestCase { let startUploadExpectation = self.expectation(description: "Upload has started") var mockDataUploader = DataUploaderMock(uploadStatus: .mockWith(needsRetry: true)) - mockDataUploader.onUpload = { - startUploadExpectation.fulfill() - } + mockDataUploader.onUpload = { startUploadExpectation.fulfill() } // Given writer.write(value: ["key": "value"]) diff --git a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift index 8137676e7e..67fbc37b0f 100644 --- a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift +++ b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift @@ -50,7 +50,7 @@ internal class DatadogTestsObserver: NSObject, XCTestObservation { && CrashReportingFeature.instance == nil && InternalMonitoringFeature.instance == nil }, - problem: "All features not be initialized.", + problem: "All features must not be initialized.", solution: """ Make sure `{Feature}.instance?.deinitialize()` is called before the end of test that uses `{Feature}.instance` mock. """ @@ -59,7 +59,7 @@ internal class DatadogTestsObserver: NSObject, XCTestObservation { assert: { RUMAutoInstrumentation.instance == nil && URLSessionAutoInstrumentation.instance == nil }, - problem: "All auto-instrumentation features not be initialized.", + problem: "All auto-instrumentation features must not be initialized.", solution: """ Make sure `{AutoInstrumentationFeature}.instance?.deinitialize()` is called before the end of test that uses `{AutoInstrumentationFeature}.instance` mock. From 514885d0aa13b7772e855c3ed288935f8c157c94 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 16:17:32 +0200 Subject: [PATCH 49/80] RUMM-1614 Upgrade PLCR to `1.10.0` --- Cartfile | 2 +- Cartfile.resolved | 2 +- DatadogSDKCrashReporting.podspec | 2 +- DatadogSDKCrashReporting.podspec.src | 2 +- Package.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cartfile b/Cartfile index 44747c45a4..92d403be59 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "lyft/Kronos" ~> 4.2 -github "microsoft/plcrashreporter" == 1.8.1 +github "microsoft/plcrashreporter" == 1.10.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index fbd67e454a..03a1b34fda 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "lyft/Kronos" "4.2.1" -github "microsoft/plcrashreporter" "1.8.1" +github "microsoft/plcrashreporter" "1.10.0" diff --git a/DatadogSDKCrashReporting.podspec b/DatadogSDKCrashReporting.podspec index f8b919a66e..915fde6c3b 100644 --- a/DatadogSDKCrashReporting.podspec +++ b/DatadogSDKCrashReporting.podspec @@ -21,5 +21,5 @@ Pod::Spec.new do |s| s.source_files = "Sources/DatadogCrashReporting/**/*.swift" s.dependency 'DatadogSDK', '1.7.0-beta4' - s.dependency 'PLCrashReporter', '~> 1.8.1' + s.dependency 'PLCrashReporter', '~> 1.10.0' end diff --git a/DatadogSDKCrashReporting.podspec.src b/DatadogSDKCrashReporting.podspec.src index 7fcd894895..d975e23acf 100644 --- a/DatadogSDKCrashReporting.podspec.src +++ b/DatadogSDKCrashReporting.podspec.src @@ -21,5 +21,5 @@ Pod::Spec.new do |s| s.source_files = "Sources/DatadogCrashReporting/**/*.swift" s.dependency 'DatadogSDK', '__DATADOG_VERSION__' - s.dependency 'PLCrashReporter', '~> 1.8.1' + s.dependency 'PLCrashReporter', '~> 1.10.0' end diff --git a/Package.swift b/Package.swift index 8638cab917..b613169d67 100644 --- a/Package.swift +++ b/Package.swift @@ -36,7 +36,7 @@ let package = Package( ], dependencies: [ .package(name: "Kronos", url: "https://github.com/lyft/Kronos.git", from: "4.2.1"), - .package(name: "PLCrashReporter", url: "https://github.com/microsoft/plcrashreporter.git", from: "1.8.1"), + .package(name: "PLCrashReporter", url: "https://github.com/microsoft/plcrashreporter.git", from: "1.10.0"), ], targets: [ .target( From c12dffacaa17accfed3e3663ff7fc7208cea3a7c Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 14 Sep 2021 16:52:51 +0200 Subject: [PATCH 50/80] RUMM-1614 CR fix - do not pin to certain version of PLCR for Carthage --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 92d403be59..966ca1c9b1 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "lyft/Kronos" ~> 4.2 -github "microsoft/plcrashreporter" == 1.10.0 +github "microsoft/plcrashreporter" ~> 1.10.0 From c3aef8949943d4d0fdc2074e71a4d8b8ff88b212 Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Fri, 27 Aug 2021 14:30:18 +0200 Subject: [PATCH 51/80] RUMM-1487 Add support to US5 servers --- Sources/Datadog/DatadogConfiguration.swift | 18 ++++++++++++++++++ .../DatadogConfiguration+objc.swift | 3 +++ .../Core/FeaturesConfigurationTests.swift | 12 ++++++++++++ .../DatadogObjc/DDConfigurationTests.swift | 7 +++++-- .../ObjcAPITests/DDConfiguration+apiTests.m | 1 + 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index e2d596f682..9b5e1e3de9 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -42,6 +42,9 @@ extension Datadog { /// US based servers. /// Sends data to [app.datadoghq.com](https://us3.datadoghq.com/). case us3 + /// US based servers. + /// Sends data to [app.datadoghq.com](https://us5.datadoghq.com/). + case us5 /// Europe based servers. /// Sends data to [app.datadoghq.eu](https://app.datadoghq.eu/). case eu1 @@ -65,6 +68,7 @@ extension Datadog { switch self { case .us1: return .us1 case .us3: return .us3 + case .us5: return .us5 case .eu1: return .eu1 case .us1_fed: return .us1_fed } @@ -74,6 +78,7 @@ extension Datadog { switch self { case .us1: return .us1 case .us3: return .us3 + case .us5: return .us5 case .eu1: return .eu1 case .us1_fed: return .us1_fed } @@ -83,6 +88,7 @@ extension Datadog { switch self { case .us1: return .us1 case .us3: return .us3 + case .us5: return .us5 case .eu1: return .eu1 case .us1_fed: return .us1_fed } @@ -97,6 +103,9 @@ extension Datadog { /// US based servers. /// Sends logs to [app.datadoghq.com](https://us3.datadoghq.com/). case us3 + /// US based servers. + /// Sends logs to [app.datadoghq.com](https://us5.datadoghq.com/). + case us5 /// Europe based servers. /// Sends logs to [app.datadoghq.eu](https://app.datadoghq.eu/). case eu1 @@ -120,6 +129,7 @@ extension Datadog { switch self { case .us1, .us: return "https://logs.browser-intake-datadoghq.com/" + endpoint case .us3: return "https://logs.browser-intake-us3-datadoghq.com/" + endpoint + case .us5: return "https://logs.browser-intake-us5-datadoghq.com/" + endpoint case .eu1, .eu: return "https://mobile-http-intake.logs.datadoghq.eu/" + endpoint case .us1_fed, .gov: return "https://logs.browser-intake-ddog-gov.com/" + endpoint case let .custom(url: url): return url @@ -135,6 +145,9 @@ extension Datadog { /// US based servers. /// Sends traces to [app.datadoghq.com](https://us3.datadoghq.com/). case us3 + /// US based servers. + /// Sends traces to [app.datadoghq.com](https://us5.datadoghq.com/). + case us5 /// Europe based servers. /// Sends traces to [app.datadoghq.eu](https://app.datadoghq.eu/). case eu1 @@ -158,6 +171,7 @@ extension Datadog { switch self { case .us1, .us: return "https://trace.browser-intake-datadoghq.com/" + endpoint case .us3: return "https://trace.browser-intake-us3-datadoghq.com/" + endpoint + case .us5: return "https://trace.browser-intake-us5-datadoghq.com/" + endpoint case .eu1, .eu: return "https:/public-trace-http-intake.logs.datadoghq.eu/" + endpoint case .us1_fed, .gov: return "https://trace.browser-intake-ddog-gov.com/" + endpoint case let .custom(url: url): return url @@ -173,6 +187,9 @@ extension Datadog { /// US based servers. /// Sends RUM events to [app.datadoghq.com](https://us3.datadoghq.com/). case us3 + /// US based servers. + /// Sends RUM events to [app.datadoghq.com](https://us5.datadoghq.com/). + case us5 /// Europe based servers. /// Sends RUM events to [app.datadoghq.eu](https://app.datadoghq.eu/). case eu1 @@ -196,6 +213,7 @@ extension Datadog { switch self { case .us1, .us: return "https://rum.browser-intake-datadoghq.com/" + endpoint case .us3: return "https://rum.browser-intake-us3-datadoghq.com/" + endpoint + case .us5: return "https://rum.browser-intake-us5-datadoghq.com/" + endpoint case .eu1, .eu: return "https://rum-http-intake.logs.datadoghq.eu/" + endpoint case .us1_fed, .gov: return "https://rum.browser-intake-ddog-gov.com/" + endpoint case let .custom(url: url): return url diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index d43e33a334..56f4247969 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -23,6 +23,9 @@ public class DDEndpoint: NSObject { @objc public static func us3() -> DDEndpoint { .init(sdkEndpoint: .us3) } + @objc + public static func us5() -> DDEndpoint { .init(sdkEndpoint: .us5) } + @objc public static func eu1() -> DDEndpoint { .init(sdkEndpoint: .eu1) } diff --git a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift index b65675746a..e10be65217 100644 --- a/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift +++ b/Tests/DatadogTests/Datadog/Core/FeaturesConfigurationTests.swift @@ -155,6 +155,10 @@ class FeaturesConfigurationTests: XCTestCase { try configuration(datadogEndpoint: .us3).logging?.uploadURL.absoluteString, "https://logs.browser-intake-us3-datadoghq.com/api/v2/logs" ) + XCTAssertEqual( + try configuration(datadogEndpoint: .us5).logging?.uploadURL.absoluteString, + "https://logs.browser-intake-us5-datadoghq.com/api/v2/logs" + ) XCTAssertEqual( try configuration(datadogEndpoint: .eu1).logging?.uploadURL.absoluteString, "https://mobile-http-intake.logs.datadoghq.eu/api/v2/logs" @@ -184,6 +188,10 @@ class FeaturesConfigurationTests: XCTestCase { try configuration(datadogEndpoint: .us3).tracing?.uploadURL.absoluteString, "https://trace.browser-intake-us3-datadoghq.com/api/v2/spans" ) + XCTAssertEqual( + try configuration(datadogEndpoint: .us5).tracing?.uploadURL.absoluteString, + "https://trace.browser-intake-us5-datadoghq.com/api/v2/spans" + ) XCTAssertEqual( try configuration(datadogEndpoint: .eu1).tracing?.uploadURL.absoluteString, "https:/public-trace-http-intake.logs.datadoghq.eu/api/v2/spans" @@ -213,6 +221,10 @@ class FeaturesConfigurationTests: XCTestCase { try configuration(datadogEndpoint: .us3).rum?.uploadURL.absoluteString, "https://rum.browser-intake-us3-datadoghq.com/api/v2/rum" ) + XCTAssertEqual( + try configuration(datadogEndpoint: .us5).rum?.uploadURL.absoluteString, + "https://rum.browser-intake-us5-datadoghq.com/api/v2/rum" + ) XCTAssertEqual( try configuration(datadogEndpoint: .eu1).rum?.uploadURL.absoluteString, "https://rum-http-intake.logs.datadoghq.eu/api/v2/rum" diff --git a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift index d3d01d5ee4..a7e9524de2 100644 --- a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift +++ b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift @@ -11,7 +11,7 @@ import XCTest extension Datadog.Configuration.LogsEndpoint: Equatable { public static func == (_ lhs: Datadog.Configuration.LogsEndpoint, _ rhs: Datadog.Configuration.LogsEndpoint) -> Bool { switch (lhs, rhs) { - case (.us, .us), (.eu, .eu), (.gov, .gov), (.us1, .us1), (.us3, .us3), (.eu1, .eu1), (.us1_fed, .us1_fed): return true + case (.us, .us), (.eu, .eu), (.gov, .gov), (.us1, .us1), (.us3, .us3), (.us5, .us5), (.eu1, .eu1), (.us1_fed, .us1_fed): return true case let (.custom(lhsURL), .custom(rhsURL)): return lhsURL == rhsURL default: return false } @@ -21,7 +21,7 @@ extension Datadog.Configuration.LogsEndpoint: Equatable { extension Datadog.Configuration.TracesEndpoint: Equatable { public static func == (_ lhs: Datadog.Configuration.TracesEndpoint, _ rhs: Datadog.Configuration.TracesEndpoint) -> Bool { switch (lhs, rhs) { - case (.us, .us), (.eu, .eu), (.gov, .gov), (.us1, .us1), (.us3, .us3), (.eu1, .eu1), (.us1_fed, .us1_fed): return true + case (.us, .us), (.eu, .eu), (.gov, .gov), (.us1, .us1), (.us3, .us3), (.us5, .us5), (.eu1, .eu1), (.us1_fed, .us1_fed): return true case let (.custom(lhsURL), .custom(rhsURL)): return lhsURL == rhsURL default: return false } @@ -100,6 +100,9 @@ class DDConfigurationTests: XCTestCase { objcBuilder.set(endpoint: .us3()) XCTAssertEqual(objcBuilder.build().sdkConfiguration.datadogEndpoint, .us3) + objcBuilder.set(endpoint: .us5()) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.datadogEndpoint, .us5) + objcBuilder.set(endpoint: .us1_fed()) XCTAssertEqual(objcBuilder.build().sdkConfiguration.datadogEndpoint, .us1_fed) diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m index e167a738b7..c01d2e47a6 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m @@ -54,6 +54,7 @@ - (void)testDDEndpointAPI { [DDEndpoint us1]; [DDEndpoint us1_fed]; [DDEndpoint us3]; + [DDEndpoint us5]; } - (void)testDDBatchSizeAPI { From 3871a950141a018f21a0bbdb7dfed285457d71cc Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 15 Sep 2021 12:31:44 +0200 Subject: [PATCH 52/80] RUMM-1373 CR fix - start `DatadogTestsObserver` explicitly --- .../TestsObserver/DatadogTestsObserver.swift | 1 + .../TestsObserver/DatadogTestsObserverLoader.m | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift index 67fbc37b0f..87e19590c4 100644 --- a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift +++ b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift @@ -8,6 +8,7 @@ import XCTest @testable import Datadog /// Observes unit tests execution and performs integrity checks after each test to ensure that the global state is unaltered. +@objc internal class DatadogTestsObserver: NSObject, XCTestObservation { @objc static func startObserving() { diff --git a/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m b/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m index 08093ef45b..aace2eff7d 100644 --- a/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m +++ b/Tests/DatadogTests/TestsObserver/DatadogTestsObserverLoader.m @@ -6,15 +6,10 @@ #import #import +#import "DatadogTests-Swift.h" /// This code runs when the `DatadogTests` bundle is loaded into memory and tests start. /// Reference: https://developer.apple.com/documentation/objectivec/nsobject/1418815-load __attribute__((constructor)) static void initialize_FrameworkLoadHandler() { - Class testsObserverClass = objc_getClass("DatadogTests.DatadogTestsObserver"); - SEL startObservingSelector = NSSelectorFromString(@"startObserving"); - NSMethodSignature *methodSignature = [testsObserverClass methodSignatureForSelector:startObservingSelector]; - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; - [invocation setTarget:testsObserverClass]; - [invocation setSelector:startObservingSelector]; - [invocation invoke]; + [DatadogTestsObserver startObserving]; } From ea67964238ad9b0862b8e81e0eaa50eccdb5d737 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 15 Sep 2021 13:55:25 +0200 Subject: [PATCH 53/80] RUMM-1599 Add debug menu for testing Crash Reporting with RUM --- Datadog/Datadog.xcodeproj/project.pbxproj | 4 + Datadog/Example/AppConfiguration.swift | 10 +- Datadog/Example/Base.lproj/Main.storyboard | 552 ++++++++++++------ ...gCrashReportingWithRUMViewController.swift | 51 ++ 4 files changed, 450 insertions(+), 167 deletions(-) create mode 100644 Datadog/Example/Debugging/DebugCrashReportingWithRUMViewController.swift diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 9e2e94e98c..80b75dc6b4 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -441,6 +441,7 @@ 61F3CDA72512144600C816E5 /* UIKitRUMViewsPredicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F3CDA62512144600C816E5 /* UIKitRUMViewsPredicate.swift */; }; 61F3CDAB25121FB500C816E5 /* UIViewControllerSwizzlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F3CDAA25121FB500C816E5 /* UIViewControllerSwizzlerTests.swift */; }; 61F3CDAD25122C9200C816E5 /* UIKitRUMViewsHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F3CDAC25122C9200C816E5 /* UIKitRUMViewsHandlerTests.swift */; }; + 61F74AF426F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F74AF326F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift */; }; 61F7F1DD266F9CB000F9F53B /* CodableValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61133BA02423979B00786299 /* CodableValue.swift */; }; 61F8CC092469295500FE2908 /* DatadogConfigurationBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F8CC082469295500FE2908 /* DatadogConfigurationBuilderTests.swift */; }; 61F9CA712512450B000A5E61 /* RUMAutoInstrumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61F9CA702512450B000A5E61 /* RUMAutoInstrumentationTests.swift */; }; @@ -1073,6 +1074,7 @@ 61F3CDA62512144600C816E5 /* UIKitRUMViewsPredicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitRUMViewsPredicate.swift; sourceTree = ""; }; 61F3CDAA25121FB500C816E5 /* UIViewControllerSwizzlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerSwizzlerTests.swift; sourceTree = ""; }; 61F3CDAC25122C9200C816E5 /* UIKitRUMViewsHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitRUMViewsHandlerTests.swift; sourceTree = ""; }; + 61F74AF326F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugCrashReportingWithRUMViewController.swift; sourceTree = ""; }; 61F8CC082469295500FE2908 /* DatadogConfigurationBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogConfigurationBuilderTests.swift; sourceTree = ""; }; 61F9CA702512450B000A5E61 /* RUMAutoInstrumentationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMAutoInstrumentationTests.swift; sourceTree = ""; }; 61F9CA782512593A000A5E61 /* RUMNavigationControllerScenario.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = RUMNavigationControllerScenario.storyboard; sourceTree = ""; }; @@ -2171,6 +2173,7 @@ 61441C942461A649003D8BB8 /* DebugLoggingViewController.swift */, 61441C932461A649003D8BB8 /* DebugTracingViewController.swift */, 61E5333724B84EE2003D6C4E /* DebugRUMViewController.swift */, + 61F74AF326F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift */, ); path = Debugging; sourceTree = ""; @@ -4081,6 +4084,7 @@ 61D50C5A2580EFF3006038A3 /* URLSessionScenarios.swift in Sources */, 614CADCE250FCA0200B93D2D /* TestScenarios.swift in Sources */, 6111542525C992F8007C84C9 /* CrashReportingScenarios.swift in Sources */, + 61F74AF426F20E4600E5F5ED /* DebugCrashReportingWithRUMViewController.swift in Sources */, 61441C972461A649003D8BB8 /* UIViewController+KeyboardControlling.swift in Sources */, 61B9ED1C2461E12000C0DCFF /* SendLogsFixtureViewController.swift in Sources */, 6111543F25C996A5007C84C9 /* CrashReportingViewController.swift in Sources */, diff --git a/Datadog/Example/AppConfiguration.swift b/Datadog/Example/AppConfiguration.swift index b1cb84e5e3..8ad86fc5b0 100644 --- a/Datadog/Example/AppConfiguration.swift +++ b/Datadog/Example/AppConfiguration.swift @@ -6,6 +6,7 @@ import UIKit import Datadog +import DatadogCrashReporting protocol AppConfiguration { /// The tracking consent value applied when initializing the SDK. @@ -45,9 +46,16 @@ struct ExampleAppConfiguration: AppConfiguration { .enableInternalMonitoring(clientToken: Environment.readClientToken()) #endif - // If the app was launched with test scenarion ENV, apply the scenario configuration if let testScenario = testScenario { + // If the `Example` app was launched with test scenario ENV, apply the scenario configuration testScenario.configureSDK(builder: configuration) + } else { + // Otherwise just enable all features so they can be tested with debug menu + _ = configuration + .enableLogging(true) + .enableTracing(true) + .enableRUM(true) + .enableCrashReporting(using: DDCrashReportingPlugin()) } return configuration.build() diff --git a/Datadog/Example/Base.lproj/Main.storyboard b/Datadog/Example/Base.lproj/Main.storyboard index 1996662b38..da6c479f16 100644 --- a/Datadog/Example/Base.lproj/Main.storyboard +++ b/Datadog/Example/Base.lproj/Main.storyboard @@ -1,10 +1,11 @@ - + - + + @@ -15,17 +16,17 @@ - + - + - + - + @@ -45,7 +46,7 @@ - + @@ -65,7 +66,7 @@ - + @@ -84,6 +85,26 @@ + + + + + + + + + + + + + + @@ -129,21 +150,21 @@ - - + + - - + + @@ -154,39 +175,39 @@ - + - - + + - - + + - + @@ -200,27 +221,27 @@ - - + + - - + + - + @@ -228,14 +249,14 @@ - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -653,21 +862,21 @@ - - + + - - + + @@ -677,30 +886,30 @@ - + - + - - + + - + @@ -709,13 +918,13 @@ - + @@ -752,30 +961,30 @@ - + - + - - + + - + @@ -784,13 +993,13 @@ - + @@ -803,13 +1012,13 @@ - + @@ -846,30 +1055,30 @@ - + - + - - + + - + @@ -878,13 +1087,13 @@ - + @@ -897,13 +1106,13 @@ - + @@ -940,30 +1149,30 @@ - + - + - - + + - + @@ -972,13 +1181,13 @@ - + @@ -991,13 +1200,13 @@ - + @@ -1034,50 +1243,50 @@ - - + + - - + + - - + + - - - + + + - + + - @@ -1101,4 +1310,15 @@ + + + + + + + + + + + diff --git a/Datadog/Example/Debugging/DebugCrashReportingWithRUMViewController.swift b/Datadog/Example/Debugging/DebugCrashReportingWithRUMViewController.swift new file mode 100644 index 0000000000..92eaa27ea9 --- /dev/null +++ b/Datadog/Example/Debugging/DebugCrashReportingWithRUMViewController.swift @@ -0,0 +1,51 @@ +/* + * 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. + */ + +import UIKit +import Datadog + +class DebugCrashReportingWithRUMViewController: UIViewController { + @IBOutlet weak var rumServiceNameTextField: UITextField! + + override func viewDidLoad() { + super.viewDidLoad() + rumServiceNameTextField.text = (appConfiguration as? ExampleAppConfiguration)?.serviceName + + viewNameTextField.placeholder = viewName + } + + private func crash() { + let objc = CrashReportingObjcHelpers() + objc.throwUncaughtNSException() + } + + // MARK: - Crash after starting RUM session + + @IBOutlet weak var viewNameTextField: UITextField! + + private var viewName: String { + viewNameTextField.text!.isEmpty ? "FooViewController" : viewNameTextField.text! + } + + @IBAction func didTapCrashAfterStartingRUMSession(_ sender: Any) { + (sender as? UIButton)?.disableFor(seconds: 0.5) + + rumMonitor.startView(key: viewName, name: viewName, attributes: [:]) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.crash() + } + } + + // MARK: - Crash before starting RUM session + + @IBAction func didTapCrashBeforeStartingRUMSession(_ sender: Any) { + (sender as? UIButton)?.disableFor(seconds: 0.5) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.crash() + } + } +} From 3a91214f4e267b6cb0cf5ba960355c53458827de Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 15 Sep 2021 16:41:57 +0200 Subject: [PATCH 54/80] RUMM-1373 Do not run `DatadogTests` integrity checks if test fails --- Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift index 87e19590c4..8593c941b0 100644 --- a/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift +++ b/Tests/DatadogTests/TestsObserver/DatadogTestsObserver.swift @@ -123,7 +123,9 @@ internal class DatadogTestsObserver: NSObject, XCTestObservation { ] func testCaseDidFinish(_ testCase: XCTestCase) { - performIntegrityChecks(after: testCase) + if testCase.testRun?.hasSucceeded == true { + performIntegrityChecks(after: testCase) + } } private func performIntegrityChecks(after testCase: XCTestCase) { From 91206afc7a7f840eb4c8ed1e87b1db2e4ccdad44 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Thu, 16 Sep 2021 09:23:48 +0200 Subject: [PATCH 55/80] Send nithgly unit test Slack notification to different channels depending on the status. --- tools/nightly-unit-tests/bitrise.yml.src | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/nightly-unit-tests/bitrise.yml.src b/tools/nightly-unit-tests/bitrise.yml.src index 8237672709..374a4ee66a 100644 --- a/tools/nightly-unit-tests/bitrise.yml.src +++ b/tools/nightly-unit-tests/bitrise.yml.src @@ -68,7 +68,8 @@ workflows: - slack: run_if: '{{(getenv "DD_SKIP_SLACK") | eq ""}}' inputs: - - channel: '#dd-sdk-ios' + - channel: '#mobile-ci-ios' + - channel_on_error: '#dd-sdk-ios' - fields: |- macOS|## ## Commit|${GIT_CLONE_COMMIT_HASH} From 2acd996f122e7828a5624190d28c510780d29c4f Mon Sep 17 00:00:00 2001 From: maxep Date: Thu, 16 Sep 2021 15:38:09 +0200 Subject: [PATCH 56/80] RUMM-1575 Set `Proxy-Authorization` header with proxy configuration --- Sources/Datadog/Core/Upload/HTTPClient.swift | 23 +++++++++++++++++-- .../Datadog/Core/Upload/HTTPClientTests.swift | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Sources/Datadog/Core/Upload/HTTPClient.swift b/Sources/Datadog/Core/Upload/HTTPClient.swift index 8a028ffa4f..61129245e4 100644 --- a/Sources/Datadog/Core/Upload/HTTPClient.swift +++ b/Sources/Datadog/Core/Upload/HTTPClient.swift @@ -18,8 +18,16 @@ internal class HTTPClient { configuration.urlCache = nil configuration.connectionProxyDictionary = proxyConfiguration - // TODO: RUMM-123 Optimize `URLSessionConfiguration` for good traffic performance - // and move session configuration constants to `PerformancePreset`. + // URLSession does not set the `Proxy-Authorization` header automatically when using a proxy + // configuration. We manually set the HTTP basic authentication header. + if + let user = proxyConfiguration?[kCFProxyUsernameKey] as? String, + let password = proxyConfiguration?[kCFProxyPasswordKey] as? String + { + let authorization = basicHTTPAuthentication(username: user, password: password) + configuration.httpAdditionalHeaders = ["Proxy-Authorization": authorization] + } + self.init(session: URLSession(configuration: configuration)) } @@ -39,6 +47,17 @@ internal class HTTPClient { /// The code execution in `URLSessionTransport` should never reach its initialization. internal struct URLSessionTransportInconsistencyException: Error {} +/// Returns a `Basic` `Authorization` header using the `username` and `password` provided. +/// +/// - Parameters: +/// - username: The username of the header. +/// - password: The password of the header. +/// - Returns: The HTTP Basic authentication header value +private func basicHTTPAuthentication(username: String, password: String) -> String { + let credential = Data("\(username):\(password)".utf8).base64EncodedString() + return "Basic \(credential)" +} + /// As `URLSession` returns 3-values-tuple for request execution, this function applies consistency constraints and turns /// it into only two possible states of `HTTPTransportResult`. private func httpClientResult(for urlSessionTaskCompletion: (Data?, URLResponse?, Error?)) -> Result { diff --git a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift index e011afa88e..adb4e41814 100644 --- a/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift +++ b/Tests/DatadogTests/Datadog/Core/Upload/HTTPClientTests.swift @@ -64,5 +64,9 @@ class HTTPClientTests: XCTestCase { XCTAssertEqual(actualProxy[kCFNetworkProxiesHTTPProxy] as? String, "www.example.com") XCTAssertEqual(actualProxy[kCFProxyUsernameKey] as? String, "proxyuser") XCTAssertEqual(actualProxy[kCFProxyPasswordKey] as? String, "proxypass") + XCTAssertEqual( + client.session.configuration.httpAdditionalHeaders?["Proxy-Authorization"] as? String, + "Basic cHJveHl1c2VyOnByb3h5cGFzcw==" // Base64.encode(proxyuser:proxypass) + ) } } From 602db066c0917d099cad69d86055fc6052791698 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Thu, 16 Sep 2021 10:21:53 +0200 Subject: [PATCH 57/80] RUMM-1086 Long tasks added RunLoopObservers added to RunLoop.main they observe the intervals between run loop activities and report long intervals. --- Datadog/Datadog.xcodeproj/project.pbxproj | 32 ++ .../xcshareddata/xcschemes/Example.xcscheme | 5 + .../RUMMobileVitalsScenario.storyboard | 58 ++ .../RUMMobileVitalsViewController.swift | 28 + .../Example/Scenarios/RUM/RUMScenarios.swift | 14 + .../Datadog/Core/FeaturesConfiguration.swift | 10 +- Sources/Datadog/DatadogConfiguration.swift | 19 + .../CrashReportingWithRUMIntegration.swift | 4 + .../LongTasks/LongTaskObserver.swift | 83 +++ .../RUMAutoInstrumentation.swift | 10 + .../RUM/DataModels/RUMDataModels.swift | 279 +++++++++- .../RUM/RUMEvent/RUMEventEncoder.swift | 2 + Sources/Datadog/RUM/RUMFeature.swift | 8 +- .../Datadog/RUM/RUMMonitor/RUMCommand.swift | 9 + .../RUMMonitor/Scopes/RUMResourceScope.swift | 2 + .../Scopes/RUMUserActionScope.swift | 8 +- .../RUM/RUMMonitor/Scopes/RUMViewScope.swift | 61 ++- .../RUM/RUMVitals/VitalInfoSampler.swift | 2 + .../RUM/Scrubbing/RUMEventsMapper.swift | 4 + .../DatadogObjc/RUM/RUMDataModels+objc.swift | 495 +++++++++++++++++- .../RUM/RUMMobileVitalsScenarioTests.swift | 64 +++ .../DatadogConfigurationBuilderTests.swift | 7 + .../Datadog/Mocks/CoreMocks.swift | 2 + .../Datadog/Mocks/RUMDataModelMocks.swift | 26 + .../Datadog/Mocks/RUMFeatureMocks.swift | 6 +- .../RUMAutoInstrumentationTests.swift | 32 +- .../Scopes/RUMUserActionScopeTests.swift | 34 ++ .../RUMMonitor/Scopes/RUMViewScopeTests.swift | 47 ++ .../RUM/Scrubbing/RUMEventsMapperTests.swift | 25 +- .../RUMModelsGenerator.swift | 3 +- 30 files changed, 1361 insertions(+), 18 deletions(-) create mode 100644 Datadog/Example/Scenarios/RUM/MobileVitals/RUMMobileVitalsScenario.storyboard create mode 100644 Datadog/Example/Scenarios/RUM/MobileVitals/RUMMobileVitalsViewController.swift create mode 100644 Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift create mode 100644 Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index 67609f2820..cded8930bc 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -473,6 +473,7 @@ 9E26E6B924C87693000B3270 /* RUMDataModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E26E6B824C87693000B3270 /* RUMDataModels.swift */; }; 9E2EF44F2694FA14008A7DAE /* VitalInfoSamplerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2EF44E2694FA14008A7DAE /* VitalInfoSamplerTests.swift */; }; 9E307C3224C8846D0039607E /* RUMDataModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E26E6B824C87693000B3270 /* RUMDataModels.swift */; }; + 9E359F4E26CD518D001E25E9 /* LongTaskObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E359F4D26CD518D001E25E9 /* LongTaskObserver.swift */; }; 9E36D92224373EA700BFBDB7 /* SwiftExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E36D92124373EA700BFBDB7 /* SwiftExtensionsTests.swift */; }; 9E544A4F24753C6E00E83072 /* MethodSwizzler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E544A4E24753C6E00E83072 /* MethodSwizzler.swift */; }; 9E544A5124753DDE00E83072 /* MethodSwizzlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E544A5024753DDE00E83072 /* MethodSwizzlerTests.swift */; }; @@ -485,6 +486,9 @@ 9E989A4225F640D100235FC3 /* AppStateListenerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E989A4125F640D100235FC3 /* AppStateListenerTests.swift */; }; 9E9973F1268DF69500D8059B /* VitalInfoSampler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9973F0268DF69500D8059B /* VitalInfoSampler.swift */; }; 9EA3CA6926775A3500B16871 /* VitalRefreshRateReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA3CA6826775A3500B16871 /* VitalRefreshRateReader.swift */; }; + 9EC2835A26CFEE0B00FACF1C /* RUMMobileVitalsScenarioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC2835926CFEE0B00FACF1C /* RUMMobileVitalsScenarioTests.swift */; }; + 9EC2835E26CFF57A00FACF1C /* RUMMobileVitalsScenario.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9EC2835D26CFF57A00FACF1C /* RUMMobileVitalsScenario.storyboard */; }; + 9EC2836026CFF59400FACF1C /* RUMMobileVitalsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC2835F26CFF59400FACF1C /* RUMMobileVitalsViewController.swift */; }; 9EC8B5DA2668197B000F7529 /* VitalCPUReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC8B5D92668197B000F7529 /* VitalCPUReader.swift */; }; 9EC8B5EE2668E4DB000F7529 /* VitalCPUReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC8B5ED2668E4DB000F7529 /* VitalCPUReaderTests.swift */; }; 9ED6A6B425F2901800CB2E29 /* AppStateListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ED6A6B325F2901800CB2E29 /* AppStateListener.swift */; }; @@ -1108,6 +1112,7 @@ 9E0542CA25F8EBBE007A3D0B /* Kronos.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Kronos.xcframework; path = ../Carthage/Build/Kronos.xcframework; sourceTree = ""; }; 9E26E6B824C87693000B3270 /* RUMDataModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RUMDataModels.swift; sourceTree = ""; }; 9E2EF44E2694FA14008A7DAE /* VitalInfoSamplerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalInfoSamplerTests.swift; sourceTree = ""; }; + 9E359F4D26CD518D001E25E9 /* LongTaskObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongTaskObserver.swift; sourceTree = ""; }; 9E36D92124373EA700BFBDB7 /* SwiftExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftExtensionsTests.swift; sourceTree = ""; }; 9E544A4E24753C6E00E83072 /* MethodSwizzler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodSwizzler.swift; sourceTree = ""; }; 9E544A5024753DDE00E83072 /* MethodSwizzlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MethodSwizzlerTests.swift; sourceTree = ""; }; @@ -1121,6 +1126,9 @@ 9E9973F0268DF69500D8059B /* VitalInfoSampler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalInfoSampler.swift; sourceTree = ""; }; 9E9EB37624468CE90002C80B /* Datadog.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Datadog.modulemap; sourceTree = ""; }; 9EA3CA6826775A3500B16871 /* VitalRefreshRateReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalRefreshRateReader.swift; sourceTree = ""; }; + 9EC2835926CFEE0B00FACF1C /* RUMMobileVitalsScenarioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMobileVitalsScenarioTests.swift; sourceTree = ""; }; + 9EC2835D26CFF57A00FACF1C /* RUMMobileVitalsScenario.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = RUMMobileVitalsScenario.storyboard; sourceTree = ""; }; + 9EC2835F26CFF59400FACF1C /* RUMMobileVitalsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMobileVitalsViewController.swift; sourceTree = ""; }; 9EC8B5D92668197B000F7529 /* VitalCPUReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalCPUReader.swift; sourceTree = ""; }; 9EC8B5ED2668E4DB000F7529 /* VitalCPUReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalCPUReaderTests.swift; sourceTree = ""; }; 9ED6A6B325F2901800CB2E29 /* AppStateListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStateListener.swift; sourceTree = ""; }; @@ -1942,6 +1950,7 @@ 61337036250F84F100236D58 /* RUM */ = { isa = PBXGroup; children = ( + 9EC2835C26CFF56B00FACF1C /* MobileVitals */, 61D50C532580EF41006038A3 /* RUMScenarios.swift */, 61337037250F84FD00236D58 /* ManualInstrumentation */, 61F9CA7725125918000A5E61 /* NavigationControllerAutoInstrumentation */, @@ -2330,6 +2339,7 @@ 61F3CDA1251118DD00C816E5 /* Views */, 6141014D251A578D00E3C2D9 /* Actions */, 6157FA5C252767B3009A8A3B /* Resources */, + 9E06058F26EF904200F5F935 /* LongTasks */, ); path = AutoInstrumentation; sourceTree = ""; @@ -3024,6 +3034,7 @@ 61163C49252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift */, 6164AF2D252C9C51000D78C4 /* RUMResourcesScenarioTests.swift */, 612D8F8025AF1C74000E2E09 /* RUMScrubbingScenarioTests.swift */, + 9EC2835926CFEE0B00FACF1C /* RUMMobileVitalsScenarioTests.swift */, ); path = RUM; sourceTree = ""; @@ -3119,6 +3130,14 @@ path = RUMEventOutputs; sourceTree = ""; }; + 9E06058F26EF904200F5F935 /* LongTasks */ = { + isa = PBXGroup; + children = ( + 9E359F4D26CD518D001E25E9 /* LongTaskObserver.swift */, + ); + path = LongTasks; + sourceTree = ""; + }; 9E47010324471027000073A4 /* include */ = { isa = PBXGroup; children = ( @@ -3155,6 +3174,15 @@ path = ../Sources/_Datadog_Private; sourceTree = ""; }; + 9EC2835C26CFF56B00FACF1C /* MobileVitals */ = { + isa = PBXGroup; + children = ( + 9EC2835D26CFF57A00FACF1C /* RUMMobileVitalsScenario.storyboard */, + 9EC2835F26CFF59400FACF1C /* RUMMobileVitalsViewController.swift */, + ); + path = MobileVitals; + sourceTree = ""; + }; 9EF49F1524476FBD004F2CA0 /* DatadogIntegrationTests */ = { isa = PBXGroup; children = ( @@ -3562,6 +3590,7 @@ 6137E649252DD88D00720485 /* RUMModalViewsAutoInstrumentationScenario.storyboard in Resources */, 61441C0C24616DE9003D8BB8 /* Main.storyboard in Resources */, 615AAC36251E353700C89EE9 /* RUMTabBarAutoInstrumentationScenario.storyboard in Resources */, + 9EC2835E26CFF57A00FACF1C /* RUMMobileVitalsScenario.storyboard in Resources */, 6167AD19251A27B80012B4D0 /* URLSessionScenario.storyboard in Resources */, 61F9CA792512593A000A5E61 /* RUMNavigationControllerScenario.storyboard in Resources */, 61337035250F84BF00236D58 /* TracingManualInstrumentationScenario.storyboard in Resources */, @@ -3850,6 +3879,7 @@ 61C3E63724BF191F008053F2 /* RUMScope.swift in Sources */, 61133BE52423979B00786299 /* LogBuilder.swift in Sources */, 61133BD42423979B00786299 /* FileReader.swift in Sources */, + 9E359F4E26CD518D001E25E9 /* LongTaskObserver.swift in Sources */, 61C5A88A24509A0C00DA608C /* SpanFileOutput.swift in Sources */, 61C3E63524BF1794008053F2 /* Attributes.swift in Sources */, 61133BD32423979B00786299 /* File.swift in Sources */, @@ -4124,6 +4154,7 @@ 61E5333824B84EE2003D6C4E /* DebugRUMViewController.swift in Sources */, 615C3155251C9D7A0018781C /* RUMTASVariousUIControllsViewController.swift in Sources */, 61441C9D2461A796003D8BB8 /* AppConfiguration.swift in Sources */, + 9EC2836026CFF59400FACF1C /* RUMMobileVitalsViewController.swift in Sources */, 6164AF06252C9004000D78C4 /* ObjcSendFirstPartyRequestsViewController.m in Sources */, 6167AD20251A27CC0012B4D0 /* SendFirstPartyRequestsViewController.swift in Sources */, 6193DCE8251B9AB1009B8011 /* RUMTASCollectionViewController.swift in Sources */, @@ -4156,6 +4187,7 @@ 61B9ED212462089600C0DCFF /* TracingManualInstrumentationScenarioTests.swift in Sources */, 61B6811F25F0EA860015B4AF /* CrashReportingWithLoggingScenarioTests.swift in Sources */, 61C2C20D24C1831700C0321C /* RUMManualInstrumentationScenarioTests.swift in Sources */, + 9EC2835A26CFEE0B00FACF1C /* RUMMobileVitalsScenarioTests.swift in Sources */, 61FF282924B8A31E000B3D9B /* RUMEventMatcher.swift in Sources */, 61F7F1DD266F9CB000F9F53B /* CodableValue.swift in Sources */, 61B9ED1F2461E57700C0DCFF /* UITestsHelpers.swift in Sources */, diff --git a/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Example.xcscheme index 4bf2dd92fd..0248c5a34c 100644 --- a/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Example.xcscheme +++ b/Datadog/Datadog.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -56,6 +56,11 @@ value = "LoggingManualInstrumentationScenario" isEnabled = "NO"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Datadog/Example/Scenarios/RUM/MobileVitals/RUMMobileVitalsViewController.swift b/Datadog/Example/Scenarios/RUM/MobileVitals/RUMMobileVitalsViewController.swift new file mode 100644 index 0000000000..afcf601264 --- /dev/null +++ b/Datadog/Example/Scenarios/RUM/MobileVitals/RUMMobileVitalsViewController.swift @@ -0,0 +1,28 @@ +/* + * 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. + */ + +import UIKit + +final class RUMMobileVitalsViewController: UIViewController { + + @IBAction func noOpButtonTapped(_ sender: Any) { + print("No-op button tapped... Sending view update...") + } + + @IBAction func blockMainThreadButtonTapped(_ sender: Any) { + print("❌ Blocking main thread at \(Date())...") + let startDate = Date() + var i = 1 + while true { + i += 1 + if Date().timeIntervalSince(startDate) > 3.0 { + print("✅ Main thread is unblocked!") + break + } + } + } + +} diff --git a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift index 1369028d03..97d90b8939 100644 --- a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift +++ b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift @@ -121,6 +121,20 @@ final class RUMTapActionScenario: TestScenario { } } +/// Scenario which uses RUM only. Blocks the main thread and expects to have non-zero MobileVitals values +final class RUMMobileVitalsScenario: TestScenario { + static var storyboardName: String = "RUMMobileVitalsScenario" + + func configureSDK(builder: Datadog.Configuration.Builder) { + _ = builder + .trackUIKitRUMViews() + .trackUIKitRUMActions() + .trackLongTasks() + .enableLogging(false) + .enableTracing(false) + } +} + /// Scenario which uses RUM and Tracing auto instrumentation features to track bunch of network requests /// sent with `URLSession` from two VCs. The first VC calls first party resources, the second one calls third parties. final class RUMURLSessionResourcesScenario: URLSessionBaseScenario, TestScenario { diff --git a/Sources/Datadog/Core/FeaturesConfiguration.swift b/Sources/Datadog/Core/FeaturesConfiguration.swift index 65a9ff9205..465f2add48 100644 --- a/Sources/Datadog/Core/FeaturesConfiguration.swift +++ b/Sources/Datadog/Core/FeaturesConfiguration.swift @@ -39,6 +39,7 @@ internal struct FeaturesConfiguration { struct AutoInstrumentation { let uiKitRUMViewsPredicate: UIKitRUMViewsPredicate? let uiKitRUMUserActionsPredicate: UIKitRUMUserActionsPredicate? + let longTaskThreshold: TimeInterval? } let common: Common @@ -50,6 +51,7 @@ internal struct FeaturesConfiguration { let resourceEventMapper: RUMResourceEventMapper? let actionEventMapper: RUMActionEventMapper? let errorEventMapper: RUMErrorEventMapper? + let longTaskEventMapper: RUMLongTaskEventMapper? /// RUM auto instrumentation configuration, `nil` if not enabled. let autoInstrumentation: AutoInstrumentation? let backgroundEventTrackingEnabled: Bool @@ -182,10 +184,13 @@ extension FeaturesConfiguration { if configuration.rumEnabled { var autoInstrumentation: RUM.AutoInstrumentation? - if configuration.rumUIKitViewsPredicate != nil || configuration.rumUIKitUserActionsPredicate != nil { + if configuration.rumUIKitViewsPredicate != nil || + configuration.rumUIKitUserActionsPredicate != nil || + configuration.rumLongTaskDurationThreshold != nil { autoInstrumentation = RUM.AutoInstrumentation( uiKitRUMViewsPredicate: configuration.rumUIKitViewsPredicate, - uiKitRUMUserActionsPredicate: configuration.rumUIKitUserActionsPredicate + uiKitRUMUserActionsPredicate: configuration.rumUIKitUserActionsPredicate, + longTaskThreshold: configuration.rumLongTaskDurationThreshold ) } @@ -200,6 +205,7 @@ extension FeaturesConfiguration { resourceEventMapper: configuration.rumResourceEventMapper, actionEventMapper: configuration.rumActionEventMapper, errorEventMapper: configuration.rumErrorEventMapper, + longTaskEventMapper: configuration.rumLongTaskEventMapper, autoInstrumentation: autoInstrumentation, backgroundEventTrackingEnabled: configuration.rumBackgroundEventTrackingEnabled, onSessionStart: configuration.rumSessionsListener diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index 9b5e1e3de9..a1b3eb1779 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -254,10 +254,12 @@ extension Datadog { private(set) var rumSessionsListener: RUMSessionListener? private(set) var rumUIKitViewsPredicate: UIKitRUMViewsPredicate? private(set) var rumUIKitUserActionsPredicate: UIKitRUMUserActionsPredicate? + private(set) var rumLongTaskDurationThreshold: TimeInterval? private(set) var rumViewEventMapper: RUMViewEventMapper? private(set) var rumResourceEventMapper: RUMResourceEventMapper? private(set) var rumActionEventMapper: RUMActionEventMapper? private(set) var rumErrorEventMapper: RUMErrorEventMapper? + private(set) var rumLongTaskEventMapper: RUMLongTaskEventMapper? private(set) var rumResourceAttributesProvider: URLSessionRUMAttributesProvider? private(set) var rumBackgroundEventTrackingEnabled: Bool private(set) var batchSize: BatchSize @@ -594,6 +596,14 @@ extension Datadog { return self } + /// Enable long operations on the main thread to be tracked automatically. + /// Any long running operation on the main thread will appear as Long Tasks in Datadog RUM Explorer. + /// - Parameter threshold: the threshold in seconds above which a task running on the Main thread is considered as a long task (default 0.1 second) + public func trackLongTasks(threshold: TimeInterval = 0.1) -> Builder { + configuration.rumLongTaskDurationThreshold = threshold + return self + } + /// Sets the custom mapper for `RUMViewEvent`. This can be used to modify RUM View events before they are send to Datadog. /// - Parameter mapper: the closure taking `RUMViewEvent` as input and expecting `RUMViewEvent` as output. /// The implementation should obtain a mutable version of the `RUMViewEvent`, modify it and return it. @@ -633,6 +643,15 @@ extension Datadog { return self } + /// Sets the custom mapper for `RUMLongTaskEvent`. This can be used to modify RUM Long Task events before they are send to Datadog. + /// - Parameter mapper: the closure taking `RUMLongTaskEvent` as input and expecting `RUMLongTaskEvent` or `nil` as output. + /// The implementation should obtain a mutable version of the `RUMLongTaskEvent`, modify it and return. Returning `nil` will result + /// with dropping the RUM Long Task event entirely, so it won't be send to Datadog. + public func setRUMLongTaskEventMapper(_ mapper: @escaping (RUMLongTaskEvent) -> RUMLongTaskEvent?) -> Builder { + configuration.rumLongTaskEventMapper = mapper + return self + } + /// Sets a closure to provide custom attributes for intercepted RUM Resources. /// /// The `provider` closure is called for each `URLSession` task intercepted by the SDK (each automatically collected RUM Resource). diff --git a/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift b/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift index 6e034ac40f..ac93b65881 100644 --- a/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift +++ b/Sources/Datadog/FeaturesIntegration/CrashReporting/CrashReportingWithRUMIntegration.swift @@ -120,6 +120,7 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { id: lastRUMView.session.id, type: .user ), + synthetics: nil, usr: lastRUMView.usr, view: .init( id: lastRUMView.view.id, @@ -149,6 +150,7 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { date: crashDate.timeIntervalSince1970.toInt64Milliseconds, service: original.service, session: original.session, + synthetics: nil, usr: original.usr, view: .init( action: original.view.action, @@ -164,9 +166,11 @@ internal struct CrashReportingWithRUMIntegration: CrashReportingIntegration { firstContentfulPaint: original.view.firstContentfulPaint, firstInputDelay: original.view.firstInputDelay, firstInputTime: original.view.firstInputTime, + frozenFrame: nil, id: original.view.id, inForegroundPeriods: original.view.inForegroundPeriods, isActive: false, + isSlowRendered: nil, largestContentfulPaint: original.view.largestContentfulPaint, loadEvent: original.view.loadEvent, loadingTime: original.view.loadingTime, diff --git a/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift b/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift new file mode 100644 index 0000000000..2f726301f2 --- /dev/null +++ b/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift @@ -0,0 +1,83 @@ +/* + * 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. + */ + +import Foundation + +internal class LongTaskObserver { + let longTaskDurationThreshold: TimeInterval + let dateProvider: DateProvider + + private var observer_begin: CFRunLoopObserver? + private var observer_end: CFRunLoopObserver? + private var lastActivity: (kind: CFRunLoopActivity, date: Date)? + + weak var subscriber: RUMCommandSubscriber? + func subscribe(commandsSubscriber: RUMCommandSubscriber) { + self.subscriber = commandsSubscriber + } + + init(threshold: TimeInterval, dateProvider: DateProvider) { + self.longTaskDurationThreshold = threshold + self.dateProvider = dateProvider + + let activites_begin: [CFRunLoopActivity] = [ + .entry, + .afterWaiting, + .beforeSources, + .beforeTimers + ] + observer_begin = CFRunLoopObserverCreateWithHandler( + kCFAllocatorDefault, + CFRunLoopActivity(activites_begin).rawValue, + true, + CFIndex.min + ) { [weak self] block_obs, block_act in + guard let strongSelf = self else { + return + } + let now = strongSelf.dateProvider.currentDate() + strongSelf.processActivity(block_act, at: now) + strongSelf.lastActivity = (kind: block_act, date: now) + } + + let activites_end: [CFRunLoopActivity] = [.beforeWaiting, .exit] + observer_end = CFRunLoopObserverCreateWithHandler( + kCFAllocatorDefault, + CFRunLoopActivity(activites_end).rawValue, + true, + CFIndex.max + ) { [weak self] block_obs, block_act in + guard let strongSelf = self else { + return + } + strongSelf.processActivity(block_act, at: strongSelf.dateProvider.currentDate()) + strongSelf.lastActivity = nil + } + } + + func start() { + CFRunLoopAddObserver(RunLoop.main.getCFRunLoop(), observer_begin, .commonModes) + CFRunLoopAddObserver(RunLoop.main.getCFRunLoop(), observer_end, .commonModes) + } + + deinit { + CFRunLoopRemoveObserver(RunLoop.main.getCFRunLoop(), observer_begin, .commonModes) + CFRunLoopRemoveObserver(RunLoop.main.getCFRunLoop(), observer_end, .commonModes) + } + + private func processActivity(_ activity: CFRunLoopActivity, at date: Date) { + if let last = self.lastActivity, + date.timeIntervalSince(last.date) > self.longTaskDurationThreshold { + let duration = date.timeIntervalSince(last.date) + let longTaskCommand = RUMAddLongTaskCommand( + time: date, + attributes: [:], + duration: duration + ) + subscriber?.process(command: longTaskCommand) + } + } +} diff --git a/Sources/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentation.swift b/Sources/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentation.swift index ca24ea8c1a..fbbd93ca45 100644 --- a/Sources/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentation.swift +++ b/Sources/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentation.swift @@ -50,6 +50,8 @@ internal final class RUMAutoInstrumentation { let views: Views? /// RUM User Actions auto instrumentation, `nil` if not enabled. let userActions: UserActions? + /// RUM Long Tasks auto instrumentation, `nil` if not enabled. + let longTasks: LongTaskObserver? // MARK: - Initialization @@ -68,6 +70,12 @@ internal final class RUMAutoInstrumentation { } else { userActions = nil } + + if let threshold = configuration.longTaskThreshold { + longTasks = LongTaskObserver(threshold: threshold, dateProvider: dateProvider) + } else { + longTasks = nil + } } catch { consolePrint( "🔥 Datadog SDK error: RUM automatic tracking can't be set up due to error: \(error)" @@ -79,11 +87,13 @@ internal final class RUMAutoInstrumentation { func enable() { views?.enable() userActions?.enable() + longTasks?.start() } func subscribe(commandSubscriber: RUMCommandSubscriber) { views?.handler.subscribe(commandsSubscriber: commandSubscriber) userActions?.handler.subscribe(commandsSubscriber: commandSubscriber) + longTasks?.subscribe(commandsSubscriber: commandSubscriber) } #if DD_SDK_COMPILED_FOR_TESTING diff --git a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift index 65d319b5cd..f04e8a6d8b 100644 --- a/Sources/Datadog/RUM/DataModels/RUMDataModels.swift +++ b/Sources/Datadog/RUM/DataModels/RUMDataModels.swift @@ -31,6 +31,9 @@ public struct RUMViewEvent: RUMDataModel { /// Session properties public let session: Session + /// Synthetics properties + public let synthetics: Synthetics? + /// RUM event type public let type: String = "view" @@ -48,6 +51,7 @@ public struct RUMViewEvent: RUMDataModel { case date = "date" case service = "service" case session = "session" + case synthetics = "synthetics" case type = "type" case usr = "usr" case view = "view" @@ -121,6 +125,20 @@ public struct RUMViewEvent: RUMDataModel { } } + /// Synthetics properties + public struct Synthetics: Codable { + /// The identifier of the current Synthetics test results + public let resultId: String + + /// The identifier of the current Synthetics test + public let testId: String + + enum CodingKeys: String, CodingKey { + case resultId = "result_id" + case testId = "test_id" + } + } + /// View properties public struct View: Codable { /// Properties of the actions of the view @@ -162,6 +180,9 @@ public struct RUMViewEvent: RUMDataModel { /// Duration in ns to the first input public let firstInputTime: Int64? + /// Properties of the frozen frames of the view + public let frozenFrame: FrozenFrame? + /// UUID of the view public let id: String @@ -171,6 +192,9 @@ public struct RUMViewEvent: RUMDataModel { /// Whether the View corresponding to this event is considered active public let isActive: Bool? + /// Whether the View had a low average refresh rate + public let isSlowRendered: Bool? + /// Duration in ns to the largest contentful paint public let largestContentfulPaint: Int64? @@ -227,9 +251,11 @@ public struct RUMViewEvent: RUMDataModel { case firstContentfulPaint = "first_contentful_paint" case firstInputDelay = "first_input_delay" case firstInputTime = "first_input_time" + case frozenFrame = "frozen_frame" case id = "id" case inForegroundPeriods = "in_foreground_periods" case isActive = "is_active" + case isSlowRendered = "is_slow_rendered" case largestContentfulPaint = "largest_contentful_paint" case loadEvent = "load_event" case loadingTime = "loading_time" @@ -276,6 +302,16 @@ public struct RUMViewEvent: RUMDataModel { } } + /// Properties of the frozen frames of the view + public struct FrozenFrame: Codable { + /// Number of frozen frames that occurred on the view + public let count: Int64 + + enum CodingKeys: String, CodingKey { + case count = "count" + } + } + /// Properties of the foreground period of the view public struct InForegroundPeriods: Codable { /// Duration in ns of the view foreground period @@ -353,6 +389,9 @@ public struct RUMResourceEvent: RUMDataModel { /// Session properties public let session: Session + /// Synthetics properties + public let synthetics: Synthetics? + /// RUM event type public let type: String = "resource" @@ -372,6 +411,7 @@ public struct RUMResourceEvent: RUMDataModel { case resource = "resource" case service = "service" case session = "session" + case synthetics = "synthetics" case type = "type" case usr = "usr" case view = "view" @@ -628,6 +668,7 @@ public struct RUMResourceEvent: RUMDataModel { case font = "font" case media = "media" case other = "other" + case native = "native" } } @@ -655,6 +696,20 @@ public struct RUMResourceEvent: RUMDataModel { } } + /// Synthetics properties + public struct Synthetics: Codable { + /// The identifier of the current Synthetics test results + public let resultId: String + + /// The identifier of the current Synthetics test + public let testId: String + + enum CodingKeys: String, CodingKey { + case resultId = "result_id" + case testId = "test_id" + } + } + /// View properties public struct View: Codable { /// UUID of the view @@ -704,6 +759,9 @@ public struct RUMActionEvent: RUMDataModel { /// Session properties public let session: Session + /// Synthetics properties + public let synthetics: Synthetics? + /// RUM event type public let type: String = "action" @@ -722,6 +780,7 @@ public struct RUMActionEvent: RUMDataModel { case date = "date" case service = "service" case session = "session" + case synthetics = "synthetics" case type = "type" case usr = "usr" case view = "view" @@ -890,6 +949,20 @@ public struct RUMActionEvent: RUMDataModel { } } + /// Synthetics properties + public struct Synthetics: Codable { + /// The identifier of the current Synthetics test results + public let resultId: String + + /// The identifier of the current Synthetics test + public let testId: String + + enum CodingKeys: String, CodingKey { + case resultId = "result_id" + case testId = "test_id" + } + } + /// View properties public struct View: Codable { /// UUID of the view @@ -946,6 +1019,9 @@ public struct RUMErrorEvent: RUMDataModel { /// Session properties public let session: Session + /// Synthetics properties + public let synthetics: Synthetics? + /// RUM event type public let type: String = "error" @@ -965,6 +1041,7 @@ public struct RUMErrorEvent: RUMDataModel { case error = "error" case service = "service" case session = "session" + case synthetics = "synthetics" case type = "type" case usr = "usr" case view = "view" @@ -1161,6 +1238,20 @@ public struct RUMErrorEvent: RUMDataModel { } } + /// Synthetics properties + public struct Synthetics: Codable { + /// The identifier of the current Synthetics test results + public let resultId: String + + /// The identifier of the current Synthetics test + public let testId: String + + enum CodingKeys: String, CodingKey { + case resultId = "result_id" + case testId = "test_id" + } + } + /// View properties public struct View: Codable { /// UUID of the view @@ -1188,6 +1279,192 @@ public struct RUMErrorEvent: RUMDataModel { } } +/// Schema of all properties of a Long Task event +public struct RUMLongTaskEvent: RUMDataModel { + /// Internal properties + public let dd: DD + + /// Action properties + public let action: Action? + + /// Application properties + public let application: Application + + /// Device connectivity properties + public let connectivity: RUMConnectivity? + + /// User provided context + public internal(set) var context: RUMEventAttributes? + + /// Start of the event in ms from epoch + public let date: Int64 + + /// Long Task properties + public let longTask: LongTask + + /// The service name for this application + public let service: String? + + /// Session properties + public let session: Session + + /// Synthetics properties + public let synthetics: Synthetics? + + /// RUM event type + public let type: String = "long_task" + + /// User properties + public internal(set) var usr: RUMUser? + + /// View properties + public var view: View + + enum CodingKeys: String, CodingKey { + case dd = "_dd" + case action = "action" + case application = "application" + case connectivity = "connectivity" + case context = "context" + case date = "date" + case longTask = "long_task" + case service = "service" + case session = "session" + case synthetics = "synthetics" + case type = "type" + case usr = "usr" + case view = "view" + } + + /// Internal properties + public struct DD: Codable { + /// Version of the RUM event format + public let formatVersion: Int64 = 2 + + /// Session-related internal properties + public let session: Session? + + enum CodingKeys: String, CodingKey { + case formatVersion = "format_version" + case session = "session" + } + + /// Session-related internal properties + public struct Session: Codable { + /// Session plan: 1 is the 'lite' plan, 2 is the 'replay' plan + public let plan: Plan + + enum CodingKeys: String, CodingKey { + case plan = "plan" + } + + /// Session plan: 1 is the 'lite' plan, 2 is the 'replay' plan + public enum Plan: Int, Codable { + case plan1 = 1 + case plan2 = 2 + } + } + } + + /// Action properties + public struct Action: Codable { + /// UUID of the action + public let id: String + + enum CodingKeys: String, CodingKey { + case id = "id" + } + } + + /// Application properties + public struct Application: Codable { + /// UUID of the application + public let id: String + + enum CodingKeys: String, CodingKey { + case id = "id" + } + } + + /// Long Task properties + public struct LongTask: Codable { + /// Duration in ns of the long task + public let duration: Int64 + + /// UUID of the long task + public let id: String? + + /// Whether this long task is considered a frozen frame + public let isFrozenFrame: Bool? + + enum CodingKeys: String, CodingKey { + case duration = "duration" + case id = "id" + case isFrozenFrame = "is_frozen_frame" + } + } + + /// Session properties + public struct Session: Codable { + /// Whether this session has a replay + public let hasReplay: Bool? + + /// UUID of the session + public let id: String + + /// Type of the session + public let type: SessionType + + enum CodingKeys: String, CodingKey { + case hasReplay = "has_replay" + case id = "id" + case type = "type" + } + + /// Type of the session + public enum SessionType: String, Codable { + case user = "user" + case synthetics = "synthetics" + } + } + + /// Synthetics properties + public struct Synthetics: Codable { + /// The identifier of the current Synthetics test results + public let resultId: String + + /// The identifier of the current Synthetics test + public let testId: String + + enum CodingKeys: String, CodingKey { + case resultId = "result_id" + case testId = "test_id" + } + } + + /// View properties + public struct View: Codable { + /// UUID of the view + public let id: String + + /// User defined name of the view + public var name: String? + + /// URL that linked to the initial view of the page + public var referrer: String? + + /// URL of the view + public var url: String + + enum CodingKeys: String, CodingKey { + case id = "id" + case name = "name" + case referrer = "referrer" + case url = "url" + } + } +} + /// Device connectivity properties public struct RUMConnectivity: Codable { /// Cellular connectivity properties @@ -1351,4 +1628,4 @@ public enum RUMMethod: String, Codable { case patch = "PATCH" } -// Generated from https://github.com/DataDog/rum-events-format/tree/2ea84b56a4e0670b2d6e3e0c6a5fd27774ce4a3d +// Generated from https://github.com/DataDog/rum-events-format/tree/cdf9a70e6be9cfec5e9524c58abfe79a9fea1f64 diff --git a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift index 7ca3f6472f..3557de9df7 100644 --- a/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift +++ b/Sources/Datadog/RUM/RUMEvent/RUMEventEncoder.swift @@ -69,3 +69,5 @@ extension RUMActionEvent: RUMSanitizableEvent {} extension RUMResourceEvent: RUMSanitizableEvent {} extension RUMErrorEvent: RUMSanitizableEvent {} + +extension RUMLongTaskEvent: RUMSanitizableEvent {} diff --git a/Sources/Datadog/RUM/RUMFeature.swift b/Sources/Datadog/RUM/RUMFeature.swift index ea504091f5..29d4ac5b5c 100644 --- a/Sources/Datadog/RUM/RUMFeature.swift +++ b/Sources/Datadog/RUM/RUMFeature.swift @@ -37,9 +37,9 @@ internal final class RUMFeature { let carrierInfoProvider: CarrierInfoProviderType let launchTimeProvider: LaunchTimeProviderType - let vitalCPUReader: SamplingBasedVitalReader // VitalCPUReader - let vitalMemoryReader: SamplingBasedVitalReader // VitalMemoryReader - let vitalRefreshRateReader: ContinuousVitalReader // VitalRefreshRateReader + let vitalCPUReader: SamplingBasedVitalReader + let vitalMemoryReader: SamplingBasedVitalReader + let vitalRefreshRateReader: ContinuousVitalReader let onSessionStart: RUMSessionListener? @@ -124,6 +124,7 @@ internal final class RUMFeature { errorEventMapper: configuration.errorEventMapper, resourceEventMapper: configuration.resourceEventMapper, actionEventMapper: configuration.actionEventMapper, + longTaskEventMapper: configuration.longTaskEventMapper, internalMonitor: internalMonitor ) let storage = RUMFeature.createStorage( @@ -180,7 +181,6 @@ internal final class RUMFeature { self.vitalCPUReader = vitalCPUReader self.vitalMemoryReader = vitalMemoryReader self.vitalRefreshRateReader = vitalRefreshRateReader - self.onSessionStart = onSessionStart } diff --git a/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift b/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift index b723206b7b..77cdd345d8 100644 --- a/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift +++ b/Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift @@ -256,3 +256,12 @@ internal struct RUMAddUserActionCommand: RUMUserActionCommand { let actionType: RUMUserActionType let name: String } + +// MARK: - RUM Long Task related commands + +internal struct RUMAddLongTaskCommand: RUMCommand { + var time: Date + var attributes: [AttributeKey: AttributeValue] + + let duration: TimeInterval +} diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift index 11575623fe..a0634f93ce 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScope.swift @@ -189,6 +189,7 @@ internal class RUMResourceScope: RUMScope { ), service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( id: context.activeViewID.orNull.toRUMDataFormat, @@ -237,6 +238,7 @@ internal class RUMResourceScope: RUMScope { ), service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( id: context.activeViewID.orNull.toRUMDataFormat, diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift index b9cf66a0b7..3f8822eec4 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScope.swift @@ -42,6 +42,8 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { private var resourcesCount: UInt = 0 /// Number of Errors occured during this User Action's lifespan. private var errorsCount: UInt = 0 + /// Number of Long Tasks occured during this User Action's lifespan. + private var longTasksCount: Int64 = 0 /// Number of Resources that started but not yet ended during this User Action's lifespan. private var activeResourcesCount: Int = 0 @@ -114,6 +116,9 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { errorsCount += 1 case is RUMAddCurrentViewErrorCommand: errorsCount += 1 + case is RUMAddLongTaskCommand: + // TODO: RUMM-1616 this command is ignored if arrived after 100ms + longTasksCount += 1 default: break } @@ -136,7 +141,7 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { error: .init(count: errorsCount.toInt64), id: actionUUID.toRUMDataFormat, loadingTime: completionTime.timeIntervalSince(actionStartTime).toInt64Nanoseconds, - longTask: nil, + longTask: .init(count: longTasksCount), resource: .init(count: resourcesCount.toInt64), target: .init(name: name), type: actionType.toRUMDataFormat @@ -147,6 +152,7 @@ internal class RUMUserActionScope: RUMScope, RUMContextProvider { date: dateCorrection.applying(to: actionStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( id: context.activeViewID.orNull.toRUMDataFormat, diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift index cf83ecfc62..6a6751381f 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift @@ -10,6 +10,9 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { struct Constants { static let backgroundViewURL = "com/datadog/background/view" static let backgroundViewName = "Background" + + static let frozenFrameThresholdInNs = (0.07).toInt64Nanoseconds // 70ms + static let slowRenderingThresholdFPS = 55.0 } // MARK: - Child Scopes @@ -55,6 +58,10 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { private var resourcesCount: UInt = 0 /// Number of Errors tracked by this View. private var errorsCount: UInt = 0 + /// Number of Long Tasks tracked by this View. + private var longTasksCount: Int64 = 0 + /// Number of Frozen Frames tracked by this View. + private var frozenFramesCount: Int64 = 0 /// Current version of this View to use for RUM `documentVersion`. private var version: UInt = 0 @@ -176,6 +183,19 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { errorsCount -= 1 } + case let command as RUMAddLongTaskCommand where isActiveView: + longTasksCount += 1 + if command.duration.toInt64Nanoseconds > Constants.frozenFrameThresholdInNs { + frozenFramesCount += 1 + } + + if sendLongTaskEvent(on: command) { + needsViewUpdate = true + } else { + longTasksCount -= 1 + frozenFramesCount -= 1 + } + default: break } @@ -306,6 +326,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { date: dateCorrection.applying(to: viewStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( id: viewUUID.toRUMDataFormat, @@ -332,6 +353,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { let cpuInfo = vitalInfoSampler.cpu let memoryInfo = vitalInfoSampler.memory let refreshRateInfo = vitalInfoSampler.refreshRate + let isSlowRendered = refreshRateInfo.meanValue.flatMap { $0 < Constants.slowRenderingThresholdFPS } let eventData = RUMViewEvent( dd: .init( @@ -344,6 +366,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { date: dateCorrection.applying(to: viewStartTime).timeIntervalSince1970.toInt64Milliseconds, service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( action: .init(count: actionsCount.toInt64), @@ -361,14 +384,16 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { firstContentfulPaint: nil, firstInputDelay: nil, firstInputTime: nil, + frozenFrame: .init(count: frozenFramesCount), id: viewUUID.toRUMDataFormat, inForegroundPeriods: nil, isActive: isActiveView, + isSlowRendered: isSlowRendered, largestContentfulPaint: nil, loadEvent: nil, loadingTime: nil, loadingType: nil, - longTask: nil, + longTask: .init(count: longTasksCount), memoryAverage: memoryInfo.meanValue, memoryMax: memoryInfo.maxValue, name: viewName, @@ -416,6 +441,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { ), service: nil, session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, usr: dependencies.userInfoProvider.current, view: .init( id: context.activeViewID.orNull.toRUMDataFormat, @@ -433,6 +459,39 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { return false } + private func sendLongTaskEvent(on command: RUMAddLongTaskCommand) -> Bool { + attributes.merge(rumCommandAttributes: command.attributes) + + let taskDurationInNs = command.duration.toInt64Nanoseconds + let isFrozenFrame = taskDurationInNs > Constants.frozenFrameThresholdInNs + + let eventData = RUMLongTaskEvent( + dd: .init(session: .init(plan: .plan1)), + action: context.activeUserActionID.flatMap { RUMLongTaskEvent.Action(id: $0.toRUMDataFormat) }, + application: .init(id: context.rumApplicationID), + connectivity: dependencies.connectivityInfoProvider.current, + context: .init(contextInfo: attributes), + date: dateCorrection.applying(to: command.time).timeIntervalSince1970.toInt64Milliseconds, + longTask: .init(duration: taskDurationInNs, id: nil, isFrozenFrame: isFrozenFrame), + service: nil, + session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user), + synthetics: nil, + usr: dependencies.userInfoProvider.current, + view: .init( + id: context.activeViewID.orNull.toRUMDataFormat, + name: context.activeViewName, + referrer: nil, + url: context.activeViewPath ?? "" + ) + ) + + if let event = dependencies.eventBuilder.createRUMEvent(with: eventData) { + dependencies.eventOutput.write(rumEvent: event) + return true + } + return false + } + private func sanitizeCustomTimingName(customTiming: String) -> String { let sanitized = customTiming.replacingOccurrences(of: "[^a-zA-Z0-9_.@$-]", with: "_", options: .regularExpression) diff --git a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift index 6d0dd611f4..e6120f8e50 100644 --- a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift +++ b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift @@ -52,7 +52,9 @@ internal final class VitalInfoSampler { maximumRefreshRate: Double = Double(UIScreen.main.maximumFramesPerSecond) ) { self.cpuReader = cpuReader + self.memoryReader = memoryReader + self.refreshRateReader = refreshRateReader self.refreshRateReader.register(self.refreshRatePublisher) self.maximumRefreshRate = maximumRefreshRate diff --git a/Sources/Datadog/RUM/Scrubbing/RUMEventsMapper.swift b/Sources/Datadog/RUM/Scrubbing/RUMEventsMapper.swift index 7f858d70d9..357c0f1504 100644 --- a/Sources/Datadog/RUM/Scrubbing/RUMEventsMapper.swift +++ b/Sources/Datadog/RUM/Scrubbing/RUMEventsMapper.swift @@ -10,6 +10,7 @@ internal typealias RUMViewEventMapper = (RUMViewEvent) -> RUMViewEvent internal typealias RUMErrorEventMapper = (RUMErrorEvent) -> RUMErrorEvent? internal typealias RUMResourceEventMapper = (RUMResourceEvent) -> RUMResourceEvent? internal typealias RUMActionEventMapper = (RUMActionEvent) -> RUMActionEvent? +internal typealias RUMLongTaskEventMapper = (RUMLongTaskEvent) -> RUMLongTaskEvent? /// The `EventMapper` for RUM events. internal struct RUMEventsMapper { @@ -17,6 +18,7 @@ internal struct RUMEventsMapper { let errorEventMapper: RUMErrorEventMapper? let resourceEventMapper: RUMResourceEventMapper? let actionEventMapper: RUMActionEventMapper? + let longTaskEventMapper: RUMLongTaskEventMapper? var internalMonitor: InternalMonitor? = nil // MARK: - EventMapper @@ -33,6 +35,8 @@ internal struct RUMEventsMapper { return map(rumEvent: resourceEvent, using: resourceEventMapper) as? T case let actionEvent as RUMEvent: return map(rumEvent: actionEvent, using: actionEventMapper) as? T + case let longTaskEvent as RUMEvent: + return map(rumEvent: longTaskEvent, using: longTaskEventMapper) as? T default: internalMonitor?.sdkLogger.critical("No `RUMEventMapper` is registered for \(type(of: event))") return event diff --git a/Sources/DatadogObjc/RUM/RUMDataModels+objc.swift b/Sources/DatadogObjc/RUM/RUMDataModels+objc.swift index 7814d3a965..ab2c3defa8 100644 --- a/Sources/DatadogObjc/RUM/RUMDataModels+objc.swift +++ b/Sources/DatadogObjc/RUM/RUMDataModels+objc.swift @@ -48,6 +48,10 @@ public class DDRUMViewEvent: NSObject { DDRUMViewEventSession(root: root) } + @objc public var synthetics: DDRUMViewEventSynthetics? { + root.swiftModel.synthetics != nil ? DDRUMViewEventSynthetics(root: root) : nil + } + @objc public var type: String { root.swiftModel.type } @@ -284,6 +288,23 @@ public enum DDRUMViewEventSessionSessionType: Int { case synthetics } +@objc +public class DDRUMViewEventSynthetics: NSObject { + internal let root: DDRUMViewEvent + + internal init(root: DDRUMViewEvent) { + self.root = root + } + + @objc public var resultId: String { + root.swiftModel.synthetics!.resultId + } + + @objc public var testId: String { + root.swiftModel.synthetics!.testId + } +} + @objc public class DDRUMViewEventRUMUser: NSObject { internal let root: DDRUMViewEvent @@ -369,6 +390,10 @@ public class DDRUMViewEventView: NSObject { root.swiftModel.view.firstInputTime as NSNumber? } + @objc public var frozenFrame: DDRUMViewEventViewFrozenFrame? { + root.swiftModel.view.frozenFrame != nil ? DDRUMViewEventViewFrozenFrame(root: root) : nil + } + @objc public var id: String { root.swiftModel.view.id } @@ -381,6 +406,10 @@ public class DDRUMViewEventView: NSObject { root.swiftModel.view.isActive as NSNumber? } + @objc public var isSlowRendered: NSNumber? { + root.swiftModel.view.isSlowRendered as NSNumber? + } + @objc public var largestContentfulPaint: NSNumber? { root.swiftModel.view.largestContentfulPaint as NSNumber? } @@ -480,6 +509,19 @@ public class DDRUMViewEventViewError: NSObject { } } +@objc +public class DDRUMViewEventViewFrozenFrame: NSObject { + internal let root: DDRUMViewEvent + + internal init(root: DDRUMViewEvent) { + self.root = root + } + + @objc public var count: NSNumber { + root.swiftModel.view.frozenFrame!.count as NSNumber + } +} + @objc public class DDRUMViewEventViewInForegroundPeriods: NSObject { internal let swiftModel: RUMViewEvent.View.InForegroundPeriods @@ -610,6 +652,10 @@ public class DDRUMResourceEvent: NSObject { DDRUMResourceEventSession(root: root) } + @objc public var synthetics: DDRUMResourceEventSynthetics? { + root.swiftModel.synthetics != nil ? DDRUMResourceEventSynthetics(root: root) : nil + } + @objc public var type: String { root.swiftModel.type } @@ -1119,6 +1165,7 @@ public enum DDRUMResourceEventResourceResourceType: Int { case .font: self = .font case .media: self = .media case .other: self = .other + case .native: self = .native } } @@ -1134,6 +1181,7 @@ public enum DDRUMResourceEventResourceResourceType: Int { case .font: return .font case .media: return .media case .other: return .other + case .native: return .native } } @@ -1147,6 +1195,7 @@ public enum DDRUMResourceEventResourceResourceType: Int { case font case media case other + case native } @objc @@ -1190,6 +1239,23 @@ public enum DDRUMResourceEventSessionSessionType: Int { case synthetics } +@objc +public class DDRUMResourceEventSynthetics: NSObject { + internal let root: DDRUMResourceEvent + + internal init(root: DDRUMResourceEvent) { + self.root = root + } + + @objc public var resultId: String { + root.swiftModel.synthetics!.resultId + } + + @objc public var testId: String { + root.swiftModel.synthetics!.testId + } +} + @objc public class DDRUMResourceEventRUMUser: NSObject { internal let root: DDRUMResourceEvent @@ -1284,6 +1350,10 @@ public class DDRUMActionEvent: NSObject { DDRUMActionEventSession(root: root) } + @objc public var synthetics: DDRUMActionEventSynthetics? { + root.swiftModel.synthetics != nil ? DDRUMActionEventSynthetics(root: root) : nil + } + @objc public var type: String { root.swiftModel.type } @@ -1658,6 +1728,23 @@ public enum DDRUMActionEventSessionSessionType: Int { case synthetics } +@objc +public class DDRUMActionEventSynthetics: NSObject { + internal let root: DDRUMActionEvent + + internal init(root: DDRUMActionEvent) { + self.root = root + } + + @objc public var resultId: String { + root.swiftModel.synthetics!.resultId + } + + @objc public var testId: String { + root.swiftModel.synthetics!.testId + } +} + @objc public class DDRUMActionEventRUMUser: NSObject { internal let root: DDRUMActionEvent @@ -1760,6 +1847,10 @@ public class DDRUMErrorEvent: NSObject { DDRUMErrorEventSession(root: root) } + @objc public var synthetics: DDRUMErrorEventSynthetics? { + root.swiftModel.synthetics != nil ? DDRUMErrorEventSynthetics(root: root) : nil + } + @objc public var type: String { root.swiftModel.type } @@ -2248,6 +2339,23 @@ public enum DDRUMErrorEventSessionSessionType: Int { case synthetics } +@objc +public class DDRUMErrorEventSynthetics: NSObject { + internal let root: DDRUMErrorEvent + + internal init(root: DDRUMErrorEvent) { + self.root = root + } + + @objc public var resultId: String { + root.swiftModel.synthetics!.resultId + } + + @objc public var testId: String { + root.swiftModel.synthetics!.testId + } +} + @objc public class DDRUMErrorEventRUMUser: NSObject { internal let root: DDRUMErrorEvent @@ -2305,6 +2413,391 @@ public class DDRUMErrorEventView: NSObject { } } +@objc +public class DDRUMLongTaskEvent: NSObject { + internal var swiftModel: RUMLongTaskEvent + internal var root: DDRUMLongTaskEvent { self } + + internal init(swiftModel: RUMLongTaskEvent) { + self.swiftModel = swiftModel + } + + @objc public var dd: DDRUMLongTaskEventDD { + DDRUMLongTaskEventDD(root: root) + } + + @objc public var action: DDRUMLongTaskEventAction? { + root.swiftModel.action != nil ? DDRUMLongTaskEventAction(root: root) : nil + } + + @objc public var application: DDRUMLongTaskEventApplication { + DDRUMLongTaskEventApplication(root: root) + } + + @objc public var connectivity: DDRUMLongTaskEventRUMConnectivity? { + root.swiftModel.connectivity != nil ? DDRUMLongTaskEventRUMConnectivity(root: root) : nil + } + + @objc public var context: DDRUMLongTaskEventRUMEventAttributes? { + root.swiftModel.context != nil ? DDRUMLongTaskEventRUMEventAttributes(root: root) : nil + } + + @objc public var date: NSNumber { + root.swiftModel.date as NSNumber + } + + @objc public var longTask: DDRUMLongTaskEventLongTask { + DDRUMLongTaskEventLongTask(root: root) + } + + @objc public var service: String? { + root.swiftModel.service + } + + @objc public var session: DDRUMLongTaskEventSession { + DDRUMLongTaskEventSession(root: root) + } + + @objc public var synthetics: DDRUMLongTaskEventSynthetics? { + root.swiftModel.synthetics != nil ? DDRUMLongTaskEventSynthetics(root: root) : nil + } + + @objc public var type: String { + root.swiftModel.type + } + + @objc public var usr: DDRUMLongTaskEventRUMUser? { + root.swiftModel.usr != nil ? DDRUMLongTaskEventRUMUser(root: root) : nil + } + + @objc public var view: DDRUMLongTaskEventView { + DDRUMLongTaskEventView(root: root) + } +} + +@objc +public class DDRUMLongTaskEventDD: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var formatVersion: NSNumber { + root.swiftModel.dd.formatVersion as NSNumber + } + + @objc public var session: DDRUMLongTaskEventDDSession? { + root.swiftModel.dd.session != nil ? DDRUMLongTaskEventDDSession(root: root) : nil + } +} + +@objc +public class DDRUMLongTaskEventDDSession: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var plan: DDRUMLongTaskEventDDSessionPlan { + .init(swift: root.swiftModel.dd.session!.plan) + } +} + +@objc +public enum DDRUMLongTaskEventDDSessionPlan: Int { + internal init(swift: RUMLongTaskEvent.DD.Session.Plan) { + switch swift { + case .plan1: self = .plan1 + case .plan2: self = .plan2 + } + } + + internal var toSwift: RUMLongTaskEvent.DD.Session.Plan { + switch self { + case .plan1: return .plan1 + case .plan2: return .plan2 + } + } + + case plan1 + case plan2 +} + +@objc +public class DDRUMLongTaskEventAction: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var id: String { + root.swiftModel.action!.id + } +} + +@objc +public class DDRUMLongTaskEventApplication: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var id: String { + root.swiftModel.application.id + } +} + +@objc +public class DDRUMLongTaskEventRUMConnectivity: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var cellular: DDRUMLongTaskEventRUMConnectivityCellular? { + root.swiftModel.connectivity!.cellular != nil ? DDRUMLongTaskEventRUMConnectivityCellular(root: root) : nil + } + + @objc public var interfaces: [Int] { + root.swiftModel.connectivity!.interfaces.map { DDRUMLongTaskEventRUMConnectivityInterfaces(swift: $0).rawValue } + } + + @objc public var status: DDRUMLongTaskEventRUMConnectivityStatus { + .init(swift: root.swiftModel.connectivity!.status) + } +} + +@objc +public class DDRUMLongTaskEventRUMConnectivityCellular: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var carrierName: String? { + root.swiftModel.connectivity!.cellular!.carrierName + } + + @objc public var technology: String? { + root.swiftModel.connectivity!.cellular!.technology + } +} + +@objc +public enum DDRUMLongTaskEventRUMConnectivityInterfaces: Int { + internal init(swift: RUMConnectivity.Interfaces) { + switch swift { + case .bluetooth: self = .bluetooth + case .cellular: self = .cellular + case .ethernet: self = .ethernet + case .wifi: self = .wifi + case .wimax: self = .wimax + case .mixed: self = .mixed + case .other: self = .other + case .unknown: self = .unknown + case .none: self = .none + } + } + + internal var toSwift: RUMConnectivity.Interfaces { + switch self { + case .bluetooth: return .bluetooth + case .cellular: return .cellular + case .ethernet: return .ethernet + case .wifi: return .wifi + case .wimax: return .wimax + case .mixed: return .mixed + case .other: return .other + case .unknown: return .unknown + case .none: return .none + } + } + + case bluetooth + case cellular + case ethernet + case wifi + case wimax + case mixed + case other + case unknown + case none +} + +@objc +public enum DDRUMLongTaskEventRUMConnectivityStatus: Int { + internal init(swift: RUMConnectivity.Status) { + switch swift { + case .connected: self = .connected + case .notConnected: self = .notConnected + case .maybe: self = .maybe + } + } + + internal var toSwift: RUMConnectivity.Status { + switch self { + case .connected: return .connected + case .notConnected: return .notConnected + case .maybe: return .maybe + } + } + + case connected + case notConnected + case maybe +} + +@objc +public class DDRUMLongTaskEventRUMEventAttributes: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var contextInfo: [String: Any] { + root.swiftModel.context!.contextInfo + } +} + +@objc +public class DDRUMLongTaskEventLongTask: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var duration: NSNumber { + root.swiftModel.longTask.duration as NSNumber + } + + @objc public var id: String? { + root.swiftModel.longTask.id + } + + @objc public var isFrozenFrame: NSNumber? { + root.swiftModel.longTask.isFrozenFrame as NSNumber? + } +} + +@objc +public class DDRUMLongTaskEventSession: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var hasReplay: NSNumber? { + root.swiftModel.session.hasReplay as NSNumber? + } + + @objc public var id: String { + root.swiftModel.session.id + } + + @objc public var type: DDRUMLongTaskEventSessionSessionType { + .init(swift: root.swiftModel.session.type) + } +} + +@objc +public enum DDRUMLongTaskEventSessionSessionType: Int { + internal init(swift: RUMLongTaskEvent.Session.SessionType) { + switch swift { + case .user: self = .user + case .synthetics: self = .synthetics + } + } + + internal var toSwift: RUMLongTaskEvent.Session.SessionType { + switch self { + case .user: return .user + case .synthetics: return .synthetics + } + } + + case user + case synthetics +} + +@objc +public class DDRUMLongTaskEventSynthetics: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var resultId: String { + root.swiftModel.synthetics!.resultId + } + + @objc public var testId: String { + root.swiftModel.synthetics!.testId + } +} + +@objc +public class DDRUMLongTaskEventRUMUser: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var email: String? { + root.swiftModel.usr!.email + } + + @objc public var id: String? { + root.swiftModel.usr!.id + } + + @objc public var name: String? { + root.swiftModel.usr!.name + } + + @objc public var usrInfo: [String: Any] { + root.swiftModel.usr!.usrInfo + } +} + +@objc +public class DDRUMLongTaskEventView: NSObject { + internal let root: DDRUMLongTaskEvent + + internal init(root: DDRUMLongTaskEvent) { + self.root = root + } + + @objc public var id: String { + root.swiftModel.view.id + } + + @objc public var name: String? { + set { root.swiftModel.view.name = newValue } + get { root.swiftModel.view.name } + } + + @objc public var referrer: String? { + set { root.swiftModel.view.referrer = newValue } + get { root.swiftModel.view.referrer } + } + + @objc public var url: String { + set { root.swiftModel.view.url = newValue } + get { root.swiftModel.view.url } + } +} + // swiftlint:enable force_unwrapping -// Generated from https://github.com/DataDog/rum-events-format/tree/2ea84b56a4e0670b2d6e3e0c6a5fd27774ce4a3d +// Generated from https://github.com/DataDog/rum-events-format/tree/cdf9a70e6be9cfec5e9524c58abfe79a9fea1f64 diff --git a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift new file mode 100644 index 0000000000..05c7c1ae95 --- /dev/null +++ b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift @@ -0,0 +1,64 @@ +/* + * 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. + */ + +import HTTPServerMock +import XCTest + +private extension ExampleApplication { + func tapNoOpButton() { + buttons["No-op"].tap() + } + + func tapBlockMainThreadButton() { + buttons["Block Main Thread"].tap() + } +} + +class RUMMobileVitalsScenarioTests: IntegrationTests, RUMCommonAsserts { + func testRUMMobileVitalsScenario() throws { + // Server session recording RUM events send to `HTTPServerMock`. + let rumServerSession = server.obtainUniqueRecordingSession() + + let app = ExampleApplication() + app.launchWith( + testScenarioClassName: "RUMMobileVitalsScenario", + serverConfiguration: HTTPServerMockConfiguration( + rumEndpoint: rumServerSession.recordingURL + ) + ) + + // Wait for the app to settle down for 3 second + Thread.sleep(forTimeInterval: 3.0) + + app.tapNoOpButton() + app.tapBlockMainThreadButton() + app.tapNoOpButton() + + // Get RUM Sessions with expected number of View visits + let recordedRUMRequests = try rumServerSession.pullRecordedRequests(timeout: dataDeliveryTimeout) { requests in + let visitCount = try RUMSessionMatcher.singleSession(from: requests)?.viewVisits.count + return visitCount == 1 + } + + assertRUM(requests: recordedRUMRequests) + + let session = try XCTUnwrap(RUMSessionMatcher.singleSession(from: recordedRUMRequests)) + + XCTAssertEqual(session.viewVisits[0].viewEvents.count, 2) + let event1 = session.viewVisits[0].viewEvents[0].view + let event2 = session.viewVisits[0].viewEvents[1].view + + let longTask1 = try XCTUnwrap(event1.longTask) + let longTask2 = try XCTUnwrap(event2.longTask) + XCTAssertEqual(longTask1.count + 1, longTask2.count) + + let cpuTicksPerSec2 = try XCTUnwrap(event2.cpuTicksPerSecond) + XCTAssertGreaterThan(cpuTicksPerSec2, 0.0) + + let fps2 = try XCTUnwrap(event2.refreshRateAverage) + XCTAssertGreaterThan(fps2, 0.0) + } +} diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index 6754ced971..5cdc62f711 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -48,10 +48,12 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertNil(configuration.rumSessionsListener) XCTAssertNil(configuration.rumUIKitViewsPredicate) XCTAssertNil(configuration.rumUIKitUserActionsPredicate) + XCTAssertNil(configuration.rumLongTaskDurationThreshold) XCTAssertNil(configuration.rumViewEventMapper) XCTAssertNil(configuration.rumResourceEventMapper) XCTAssertNil(configuration.rumActionEventMapper) XCTAssertNil(configuration.rumErrorEventMapper) + XCTAssertNil(configuration.rumLongTaskEventMapper) XCTAssertNil(configuration.rumResourceAttributesProvider) XCTAssertEqual(configuration.batchSize, .medium) XCTAssertEqual(configuration.uploadFrequency, .average) @@ -65,6 +67,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { let mockRUMErrorEvent: RUMErrorEvent = .mockRandom() let mockRUMResourceEvent: RUMResourceEvent = .mockRandom() let mockRUMActionEvent: RUMActionEvent = .mockRandom() + let mockRUMLongTaskEvent: RUMLongTaskEvent = .mockRandom() let mockCrashReportingPlugin = CrashReportingPluginMock() func customized(_ builder: Datadog.Configuration.Builder) -> Datadog.Configuration.Builder { @@ -84,11 +87,13 @@ class DatadogConfigurationBuilderTests: XCTestCase { .trackURLSession(firstPartyHosts: ["example.com"]) .trackUIKitRUMViews(using: UIKitRUMViewsPredicateMock()) .trackUIKitRUMActions(using: UIKitRUMUserActionsPredicateMock()) + .trackLongTasks(threshold: 100.0) .trackBackgroundEvents(false) .setRUMViewEventMapper { _ in mockRUMViewEvent } .setRUMErrorEventMapper { _ in mockRUMErrorEvent } .setRUMResourceEventMapper { _ in mockRUMResourceEvent } .setRUMActionEventMapper { _ in mockRUMActionEvent } + .setRUMLongTaskEventMapper { _ in mockRUMLongTaskEvent } .setRUMResourceAttributesProvider { _, _, _, _ in ["foo": "bar"] } .set(batchSize: .small) .set(uploadFrequency: .frequent) @@ -138,11 +143,13 @@ class DatadogConfigurationBuilderTests: XCTestCase { XCTAssertNotNil(configuration.rumSessionsListener) XCTAssertTrue(configuration.rumUIKitViewsPredicate is UIKitRUMViewsPredicateMock) XCTAssertTrue(configuration.rumUIKitUserActionsPredicate is UIKitRUMUserActionsPredicateMock) + XCTAssertEqual(configuration.rumLongTaskDurationThreshold, 100.0) XCTAssertEqual(configuration.spanEventMapper?(.mockRandom()), mockSpanEvent) XCTAssertEqual(configuration.rumViewEventMapper?(.mockRandom()), mockRUMViewEvent) XCTAssertEqual(configuration.rumResourceEventMapper?(.mockRandom()), mockRUMResourceEvent) XCTAssertEqual(configuration.rumActionEventMapper?(.mockRandom()), mockRUMActionEvent) XCTAssertEqual(configuration.rumErrorEventMapper?(.mockRandom()), mockRUMErrorEvent) + XCTAssertEqual(configuration.rumLongTaskEventMapper?(.mockRandom()), mockRUMLongTaskEvent) XCTAssertEqual(configuration.rumResourceAttributesProvider?(.mockAny(), nil, nil, nil) as? [String: String], ["foo": "bar"]) XCTAssertFalse(configuration.rumBackgroundEventTrackingEnabled) XCTAssertEqual(configuration.batchSize, .small) diff --git a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift index 26ef672069..78d50816bc 100644 --- a/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/CoreMocks.swift @@ -232,6 +232,7 @@ extension FeaturesConfiguration.RUM { resourceEventMapper: RUMResourceEventMapper? = nil, actionEventMapper: RUMActionEventMapper? = nil, errorEventMapper: RUMErrorEventMapper? = nil, + longTaskEventMapper: RUMLongTaskEventMapper? = nil, autoInstrumentation: FeaturesConfiguration.RUM.AutoInstrumentation? = nil, backgroundEventTrackingEnabled: Bool = false, onSessionStart: @escaping RUMSessionListener = mockNoOpSessionListerner() @@ -246,6 +247,7 @@ extension FeaturesConfiguration.RUM { resourceEventMapper: resourceEventMapper, actionEventMapper: actionEventMapper, errorEventMapper: errorEventMapper, + longTaskEventMapper: longTaskEventMapper, autoInstrumentation: autoInstrumentation, backgroundEventTrackingEnabled: backgroundEventTrackingEnabled, onSessionStart: onSessionStart diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift index 4249c7293c..70fa40d024 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift @@ -12,6 +12,7 @@ extension RUMViewEvent: EquatableInTests {} extension RUMResourceEvent: EquatableInTests {} extension RUMActionEvent: EquatableInTests {} extension RUMErrorEvent: EquatableInTests {} +extension RUMLongTaskEvent: EquatableInTests {} extension RUMEvent: EquatableInTests {} extension RUMUser { @@ -67,6 +68,7 @@ extension RUMViewEvent: RandomMockable { id: .mockRandom(), type: .user ), + synthetics: nil, usr: .mockRandom(), view: .init( action: .init(count: .mockRandom()), @@ -82,6 +84,7 @@ extension RUMViewEvent: RandomMockable { firstContentfulPaint: .mockRandom(), firstInputDelay: .mockRandom(), firstInputTime: .mockRandom(), + frozenFrame: .init(count: .mockRandom()), id: .mockRandom(), inForegroundPeriods: [ .init( @@ -90,6 +93,7 @@ extension RUMViewEvent: RandomMockable { ) ], isActive: .random(), + isSlowRendered: .mockRandom(), largestContentfulPaint: .mockRandom(), loadEvent: .mockRandom(), loadingTime: .mockRandom(), @@ -148,6 +152,7 @@ extension RUMResourceEvent: RandomMockable { id: .mockRandom(), type: .user ), + synthetics: nil, usr: .mockRandom(), view: .init( id: .mockRandom(), @@ -184,6 +189,7 @@ extension RUMActionEvent: RandomMockable { id: .mockRandom(), type: .user ), + synthetics: nil, usr: .mockRandom(), view: .init( id: .mockRandom(), @@ -232,6 +238,7 @@ extension RUMErrorEvent: RandomMockable { id: .mockRandom(), type: .user ), + synthetics: nil, usr: .mockRandom(), view: .init( id: .mockRandom(), @@ -242,3 +249,22 @@ extension RUMErrorEvent: RandomMockable { ) } } + +extension RUMLongTaskEvent: RandomMockable { + static func mockRandom() -> RUMLongTaskEvent { + return RUMLongTaskEvent( + dd: .init(session: .init(plan: .plan1)), + action: .init(id: .mockRandom()), + application: .init(id: .mockRandom()), + connectivity: .mockRandom(), + context: .mockRandom(), + date: .mockRandom(), + longTask: .init(duration: .mockRandom(), id: .mockRandom(), isFrozenFrame: .mockRandom()), + service: .mockRandom(), + session: .init(hasReplay: false, id: .mockRandom(), type: .user), + synthetics: nil, + usr: .mockRandom(), + view: .init(id: .mockRandom(), name: .mockRandom(), referrer: .mockRandom(), url: .mockRandom()) + ) + } +} diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift index bc78473959..f021cc6b9d 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMFeatureMocks.swift @@ -137,13 +137,15 @@ extension RUMEventsMapper { viewEventMapper: RUMViewEventMapper? = nil, errorEventMapper: RUMErrorEventMapper? = nil, resourceEventMapper: RUMResourceEventMapper? = nil, - actionEventMapper: RUMActionEventMapper? = nil + actionEventMapper: RUMActionEventMapper? = nil, + longTaskEventMapper: RUMLongTaskEventMapper? = nil ) -> RUMEventsMapper { return RUMEventsMapper( viewEventMapper: viewEventMapper, errorEventMapper: errorEventMapper, resourceEventMapper: resourceEventMapper, - actionEventMapper: actionEventMapper + actionEventMapper: actionEventMapper, + longTaskEventMapper: longTaskEventMapper ) } } diff --git a/Tests/DatadogTests/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentationTests.swift b/Tests/DatadogTests/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentationTests.swift index 92b86e2d1d..d5441ec0e5 100644 --- a/Tests/DatadogTests/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentationTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/AutoInstrumentation/RUMAutoInstrumentationTests.swift @@ -28,7 +28,8 @@ class RUMAutoInstrumentationTests: XCTestCase { RUMAutoInstrumentation.instance = RUMAutoInstrumentation( configuration: .init( uiKitRUMViewsPredicate: UIKitRUMViewsPredicateMock(), - uiKitRUMUserActionsPredicate: nil + uiKitRUMUserActionsPredicate: nil, + longTaskThreshold: nil ), dateProvider: SystemDateProvider() ) @@ -51,7 +52,8 @@ class RUMAutoInstrumentationTests: XCTestCase { RUMAutoInstrumentation.instance = RUMAutoInstrumentation( configuration: .init( uiKitRUMViewsPredicate: nil, - uiKitRUMUserActionsPredicate: UIKitRUMUserActionsPredicateMock() + uiKitRUMUserActionsPredicate: UIKitRUMUserActionsPredicateMock(), + longTaskThreshold: nil ), dateProvider: SystemDateProvider() ) @@ -66,6 +68,29 @@ class RUMAutoInstrumentationTests: XCTestCase { XCTAssertTrue(userActionsHandler?.subscriber === Global.rum) } + func testGivenRUMLongTasksAutoInstrumentationEnabled_whenRUMMonitorIsRegistered_itSubscribesAsLongTaskObserver() throws { + // Given + RUMFeature.instance = .mockNoOp() + defer { RUMFeature.instance?.deinitialize() } + + RUMAutoInstrumentation.instance = RUMAutoInstrumentation( + configuration: .init( + uiKitRUMViewsPredicate: nil, + uiKitRUMUserActionsPredicate: nil, + longTaskThreshold: 100.0 + ), + dateProvider: SystemDateProvider() + ) + defer { RUMAutoInstrumentation.instance?.deinitialize() } + + // When + Global.rum = RUMMonitor.initialize() + defer { Global.rum = DDNoopRUMMonitor() } + + // Then + XCTAssertTrue(RUMAutoInstrumentation.instance?.longTasks?.subscriber === Global.rum) + } + /// Sanity check for not-allowed configuration. func testWhenAllRUMAutoInstrumentationsDisabled_itDoesNotCreateInstrumentationComponents() throws { // Given @@ -75,7 +100,8 @@ class RUMAutoInstrumentationTests: XCTestCase { /// This configuration is not allowed by `FeaturesConfiguration` logic. We test it for sanity. let notAllowedConfiguration = FeaturesConfiguration.RUM.AutoInstrumentation( uiKitRUMViewsPredicate: nil, - uiKitRUMUserActionsPredicate: nil + uiKitRUMUserActionsPredicate: nil, + longTaskThreshold: nil ) RUMAutoInstrumentation.instance = RUMAutoInstrumentation( diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift index 2fce50e60c..f1a74680bc 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMUserActionScopeTests.swift @@ -359,6 +359,40 @@ class RUMUserActionScopeTests: XCTestCase { XCTAssertEqual(event.model.action.error?.count, 1) } + // MARK: - Long task actions + + func testWhileDiscreteUserActionIsActive_itCountsLongTasks() throws { + var currentTime = Date() + let scope = RUMUserActionScope.mockWith( + parent: parent, + dependencies: dependencies, + name: .mockAny(), + actionType: .scroll, + attributes: [:], + startTime: currentTime, + dateCorrection: .zero, + isContinuous: false + ) + + currentTime.addTimeInterval(0.05) + + XCTAssertTrue( + scope.process( + command: RUMAddLongTaskCommand(time: currentTime, attributes: [:], duration: 1.0) + ) + ) + + currentTime.addTimeInterval(RUMUserActionScope.Constants.discreteActionTimeoutDuration) + + XCTAssertFalse( + scope.process(command: RUMCommandMock(time: currentTime)), + "Discrete User Action should complete as it reached the timeout duration" + ) + + let event = try XCTUnwrap(output.recordedEvents(ofType: RUMEvent.self).last) + XCTAssertEqual(event.model.action.longTask?.count, 1) + } + // MARK: - Events sending callbacks func testGivenUserActionScopeWithEventSentCallback_whenSuccessfullySendingEvent_thenCallbackIsCalled() throws { diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index 4a6f99ede3..e7e6018909 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -630,6 +630,53 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(viewUpdate.model.view.error.count, 1, "Failed Resource should be counted as Error") } + // MARK: - Long tasks + + func testWhenLongTaskIsAdded_itSendsLongTaskEventAndViewUpdateEvent() throws { + var currentTime: Date = .mockDecember15th2019At10AMUTC() + let scope = RUMViewScope( + parent: parent, + dependencies: dependencies, + identity: mockView, + path: "UIViewController", + name: "ViewName", + attributes: [:], + customTimings: [:], + startTime: currentTime + ) + + XCTAssertTrue( + scope.process( + command: RUMStartViewCommand.mockWith(time: currentTime, attributes: ["foo": "bar"], identity: mockView, isInitialView: true) + ) + ) + + currentTime.addTimeInterval(1) + + XCTAssertTrue( + scope.process( + command: RUMAddLongTaskCommand(time: currentTime, attributes: ["foo": "bar"], duration: 1.0) + ) + ) + + let event = try XCTUnwrap(output.recordedEvents(ofType: RUMEvent.self).last) + let longTask = event.model + + XCTAssertEqual(longTask.action?.id, scope.context.activeUserActionID?.toRUMDataFormat) + XCTAssertEqual(longTask.application.id, scope.context.rumApplicationID) + XCTAssertNil(longTask.connectivity) + XCTAssertEqual(longTask.context?.contextInfo as? [String: String], ["foo": "bar"]) + XCTAssertEqual(longTask.date, Date.mockDecember15th2019At10AMUTC(addingTimeInterval: 1).timeIntervalSince1970.toInt64Milliseconds) + XCTAssertEqual(longTask.dd.session?.plan, .plan1) + XCTAssertEqual(longTask.longTask.duration, (1.0).toInt64Nanoseconds) + XCTAssertTrue(longTask.longTask.isFrozenFrame == true) + XCTAssertEqual(longTask.view.id, scope.viewUUID.toRUMDataFormat) + XCTAssertNil(longTask.synthetics) + + let viewUpdate = try XCTUnwrap(output.recordedEvents(ofType: RUMEvent.self).last) + XCTAssertEqual(viewUpdate.model.view.longTask?.count, 1) + } + // MARK: - Custom Timings Tracking func testGivenActiveView_whenCustomTimingIsRegistered_itSendsViewUpdateEvent() throws { diff --git a/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift b/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift index bcd977fcc7..59278cd20f 100644 --- a/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/Scrubbing/RUMEventsMapperTests.swift @@ -21,6 +21,9 @@ class RUMEventsMapperTests: XCTestCase { let originalActionEvent: RUMActionEvent = .mockRandom() let modifiedActionEvent: RUMActionEvent = .mockRandom() + let originalLongTaskEvent: RUMLongTaskEvent = .mockRandom() + let modifiedLongTaskEvent: RUMLongTaskEvent = .mockRandom() + // Given let mapper = RUMEventsMapper( viewEventMapper: { viewEvent in @@ -38,6 +41,10 @@ class RUMEventsMapperTests: XCTestCase { actionEventMapper: { actionEvent in XCTAssertEqual(actionEvent, originalActionEvent, "Mapper should be called with the original event.") return modifiedActionEvent + }, + longTaskEventMapper: { longTaskEvent in + XCTAssertEqual(longTaskEvent, originalLongTaskEvent, "Mapper should be called with the original event.") + return modifiedLongTaskEvent } ) @@ -46,18 +53,21 @@ class RUMEventsMapperTests: XCTestCase { let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model + let mappedLongTaskEvent = mapper.map(event: RUMEvent(model: originalLongTaskEvent))?.model // Then XCTAssertEqual(try XCTUnwrap(mappedViewEvent), modifiedViewEvent, "Mapper should return modified event.") XCTAssertEqual(try XCTUnwrap(mappedErrorEvent), modifiedErrorEvent, "Mapper should return modified event.") XCTAssertEqual(try XCTUnwrap(mappedResourceEvent), modifiedResourceEvent, "Mapper should return modified event.") XCTAssertEqual(try XCTUnwrap(mappedActionEvent), modifiedActionEvent, "Mapper should return modified event.") + XCTAssertEqual(try XCTUnwrap(mappedLongTaskEvent), modifiedLongTaskEvent, "Mapper should return modified event.") } func testGivenMappersEnabled_whenDroppingEvents_itReturnsNil() { let originalErrorEvent: RUMErrorEvent = .mockRandom() let originalResourceEvent: RUMResourceEvent = .mockRandom() let originalActionEvent: RUMActionEvent = .mockRandom() + let originalLongTaskEvent: RUMLongTaskEvent = .mockRandom() // Given let mapper = RUMEventsMapper( @@ -73,6 +83,10 @@ class RUMEventsMapperTests: XCTestCase { actionEventMapper: { actionEvent in XCTAssertEqual(actionEvent, originalActionEvent, "Mapper should be called with the original event.") return nil + }, + longTaskEventMapper: { longTaskEvent in + XCTAssertEqual(longTaskEvent, originalLongTaskEvent, "Mapper should be called with the original event.") + return nil } ) @@ -80,11 +94,13 @@ class RUMEventsMapperTests: XCTestCase { let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model + let mappedLongTaskEvent = mapper.map(event: RUMEvent(model: originalLongTaskEvent))?.model // Then XCTAssertNil(mappedErrorEvent, "Mapper should return nil.") XCTAssertNil(mappedResourceEvent, "Mapper should return nil.") XCTAssertNil(mappedActionEvent, "Mapper should return nil.") + XCTAssertNil(mappedLongTaskEvent, "Mapper should return nil.") } func testGivenMappersDisabled_whenMappingEvents_itReturnsTheirOriginalRepresentation() throws { @@ -92,13 +108,15 @@ class RUMEventsMapperTests: XCTestCase { let originalErrorEvent: RUMErrorEvent = .mockRandom() let originalResourceEvent: RUMResourceEvent = .mockRandom() let originalActionEvent: RUMActionEvent = .mockRandom() + let originalLongTaskEvent: RUMLongTaskEvent = .mockRandom() // Given let mapper = RUMEventsMapper( viewEventMapper: nil, errorEventMapper: nil, resourceEventMapper: nil, - actionEventMapper: nil + actionEventMapper: nil, + longTaskEventMapper: nil ) // When @@ -106,12 +124,14 @@ class RUMEventsMapperTests: XCTestCase { let mappedErrorEvent = mapper.map(event: RUMEvent(model: originalErrorEvent))?.model let mappedResourceEvent = mapper.map(event: RUMEvent(model: originalResourceEvent))?.model let mappedActionEvent = mapper.map(event: RUMEvent(model: originalActionEvent))?.model + let mappedLongTaskEvent = mapper.map(event: RUMEvent(model: originalLongTaskEvent))?.model // Then XCTAssertEqual(try XCTUnwrap(mappedViewEvent), originalViewEvent, "Mapper should return the original event.") XCTAssertEqual(try XCTUnwrap(mappedErrorEvent), originalErrorEvent, "Mapper should return the original event.") XCTAssertEqual(try XCTUnwrap(mappedResourceEvent), originalResourceEvent, "Mapper should return the original event.") XCTAssertEqual(try XCTUnwrap(mappedActionEvent), originalActionEvent, "Mapper should return the original event.") + XCTAssertEqual(try XCTUnwrap(mappedLongTaskEvent), originalLongTaskEvent, "Mapper should return the original event.") } func testGivenUnrecognizedEvent_whenMapping_itReturnsItsOriginalImplementation() throws { @@ -127,7 +147,8 @@ class RUMEventsMapperTests: XCTestCase { viewEventMapper: nil, errorEventMapper: { _ in nil }, resourceEventMapper: { _ in nil }, - actionEventMapper: { _ in nil } + actionEventMapper: { _ in nil }, + longTaskEventMapper: { _ in nil } ) let mappedEvent = try XCTUnwrap(mapper.map(event: originalEvent)) diff --git a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/RUMModelsGenerator.swift b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/RUMModelsGenerator.swift index 571b88a5b7..4bf6b280f5 100644 --- a/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/RUMModelsGenerator.swift +++ b/tools/rum-models-generator/Sources/RUMModelsGeneratorCore/RUMModelsGenerator.swift @@ -58,7 +58,8 @@ public class RUMModelsGenerator { schemaFiles.viewSchema, schemaFiles.resourceSchema, schemaFiles.actionSchema, - schemaFiles.errorSchema + schemaFiles.errorSchema, + schemaFiles.longTaskSchema, ] // Read ambiguous JSON schemas from `*.json` files From 8c5935ba7a58528f43cc91db4d1bbcf1582e65fa Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Fri, 17 Sep 2021 13:15:46 +0200 Subject: [PATCH 58/80] RUMM-1588 Add `feature` and `monitor` tags to all monitors --- Datadog/E2ETests/E2ETests.swift | 1 + .../Logging/LoggerBuilderE2ETests.swift | 1 + Datadog/E2ETests/Logging/LoggerE2ETests.swift | 21 +++++++++++++++++++ .../LoggingTrackingConsentE2ETests.swift | 1 + .../monitors-gen/templates/monitor-apm.tf.src | 2 +- .../templates/monitor-logs.tf.src | 2 +- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Datadog/E2ETests/E2ETests.swift b/Datadog/E2ETests/E2ETests.swift index bbcf2bc39c..022a616264 100644 --- a/Datadog/E2ETests/E2ETests.swift +++ b/Datadog/E2ETests/E2ETests.swift @@ -31,6 +31,7 @@ class E2ETests: XCTestCase { /// - common performance monitor - measures `Datadog.initialize(...)` performance: /// ```apm + /// $feature = core /// $monitor_id = sdk_initialize_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - sdk_initialize: has a high average execution time" /// $monitor_query = "avg(last_1d):avg:trace.sdk_initialize{env:instrumentation,resource_name:sdk_initialize,service:com.datadog.ios.nightly} > 0.016" diff --git a/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift b/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift index 57459a1a15..193af5d39f 100644 --- a/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift @@ -18,6 +18,7 @@ class LoggerBuilderE2ETests: E2ETests { /// - common performance monitor - measures `Logger.builder.build()` performance: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_initialize_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_initialize: has a high average execution time" /// $monitor_query = "avg(last_1d):avg:trace.logs_logger_initialize{env:instrumentation,resource_name:logs_logger_initialize,service:com.datadog.ios.nightly} > 0.016" diff --git a/Datadog/E2ETests/Logging/LoggerE2ETests.swift b/Datadog/E2ETests/Logging/LoggerE2ETests.swift index 10368af145..29bff57e97 100644 --- a/Datadog/E2ETests/Logging/LoggerE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggerE2ETests.swift @@ -32,6 +32,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_debug_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_debug_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_debug_log{env:instrumentation,resource_name:logs_logger_debug_log,service:com.datadog.ios.nightly} > 0.024" @@ -53,6 +54,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_debug_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_debug_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_debug_log_with_error{env:instrumentation,resource_name:logs_logger_debug_log_with_error*,service:com.datadog.ios.nightly} > 0.024" @@ -74,6 +76,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_info_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_info_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_info_log{env:instrumentation,resource_name:logs_logger_info_log,service:com.datadog.ios.nightly} > 0.024" @@ -95,6 +98,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_info_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_info_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_info_log_with_error{env:instrumentation,resource_name:logs_logger_info_log_with_error,service:com.datadog.ios.nightly} > 0.024" @@ -116,6 +120,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_notice_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_notice_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_notice_log{env:instrumentation,resource_name:logs_logger_notice_log,service:com.datadog.ios.nightly} > 0.024" @@ -137,6 +142,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_notice_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_notice_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_notice_log_with_error{env:instrumentation,resource_name:logs_logger_notice_log_with_error,service:com.datadog.ios.nightly} > 0.024" @@ -158,6 +164,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_warn_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_warn_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_warn_log{env:instrumentation,resource_name:logs_logger_warn_log,service:com.datadog.ios.nightly} > 0.024" @@ -179,6 +186,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_warn_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_warn_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_warn_log_with_error{env:instrumentation,resource_name:logs_logger_warn_log_with_error,service:com.datadog.ios.nightly} > 0.024" @@ -200,6 +208,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_error_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_error_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_error_log{env:instrumentation,resource_name:logs_logger_error_log,service:com.datadog.ios.nightly} > 0.024" @@ -221,6 +230,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_error_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_error_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_error_log_with_error{env:instrumentation,resource_name:logs_logger_error_log_with_error,service:com.datadog.ios.nightly} > 0.024" @@ -242,6 +252,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_critical_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_critical_log: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_critical_log{env:instrumentation,resource_name:logs_logger_critical_log,service:com.datadog.ios.nightly} > 0.024" @@ -263,6 +274,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_critical_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_critical_log_with_error: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_critical_log_with_error{env:instrumentation,resource_name:logs_logger_critical_log_with_error,service:com.datadog.ios.nightly} > 0.024" @@ -286,6 +298,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_string_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_string_attribute: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_string_attribute{env:instrumentation,resource_name:logs_logger_add_string_attribute,service:com.datadog.ios.nightly} > 0.024" @@ -311,6 +324,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_int_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_int_attribute: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_int_attribute{env:instrumentation,resource_name:logs_logger_add_int_attribute,service:com.datadog.ios.nightly} > 0.024" @@ -336,6 +350,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_double_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_double_attribute: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_double_attribute{env:instrumentation,resource_name:logs_logger_add_double_attribute,service:com.datadog.ios.nightly} > 0.024" @@ -361,6 +376,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_bool_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_bool_attribute: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_bool_attribute{env:instrumentation,resource_name:logs_logger_add_bool_attribute,service:com.datadog.ios.nightly} > 0.024" @@ -388,6 +404,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_remove_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_attribute: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_attribute{env:instrumentation,resource_name:logs_logger_remove_attribute,service:com.datadog.ios.nightly} > 0.024" @@ -423,6 +440,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_tag: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_tag{env:instrumentation,resource_name:logs_logger_add_tag,service:com.datadog.ios.nightly} > 0.024" @@ -448,6 +466,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_add_already_formatted_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_already_formatted_tag: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_already_formatted_tag{env:instrumentation,resource_name:logs_logger_add_already_formatted_tag,service:com.datadog.ios.nightly} > 0.024" @@ -475,6 +494,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_remove_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_tag: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_tag{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_tag} > 0.024" @@ -501,6 +521,7 @@ class LoggerE2ETests: E2ETests { /// /// - performance monitor: /// ```apm + /// $feature = logs /// $monitor_id = logs_logger_remove_already_formatted_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_already_formatted_tag: has a high average execution time" /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_already_formatted_tag{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_already_formatted_tag} > 0.024" diff --git a/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift b/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift index 82fe94cf55..beaa7031f1 100644 --- a/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift @@ -23,6 +23,7 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// - common performance monitor - measures `Datadog.set(trackingConsent:)` performance: /// ```apm + /// $feature = logs /// $monitor_id = sdk_set_tracking_consent_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - sdk_set_tracking_consent: has a high average execution time" /// $monitor_query = "avg(last_1d):avg:trace.sdk_set_tracking_consent{env:instrumentation,resource_name:sdk_set_tracking_consent,service:com.datadog.ios.nightly} > 0.016" diff --git a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src index c83ab5167f..8ecd60550c 100644 --- a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src +++ b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src @@ -1,7 +1,7 @@ resource "datadog_monitor" ${{monitor_id}} { name = ${{monitor_name}} type = "query alert" - tags = ["service:com.datadog.ios.nightly", "env:instrumentation", "team:rumm", "source:ios", ] + tags = ["service:com.datadog.ios.nightly", "env:instrumentation", "team:rumm", "source:ios", "feature:${{feature}}", "monitor:performance"] message = < Date: Fri, 17 Sep 2021 16:11:47 +0200 Subject: [PATCH 59/80] RUMM-1588 Update performance monitors to use span's resource name and common metric name --- Datadog/E2ETests/E2ETests.swift | 12 +-- .../Logging/LoggerBuilderE2ETests.swift | 26 +++--- Datadog/E2ETests/Logging/LoggerE2ETests.swift | 84 +++++++++---------- .../LoggingConfigurationE2ETests.swift | 8 +- .../LoggingTrackingConsentE2ETests.swift | 50 +++++------ 5 files changed, 90 insertions(+), 90 deletions(-) diff --git a/Datadog/E2ETests/E2ETests.swift b/Datadog/E2ETests/E2ETests.swift index 022a616264..cc519ed789 100644 --- a/Datadog/E2ETests/E2ETests.swift +++ b/Datadog/E2ETests/E2ETests.swift @@ -34,21 +34,21 @@ class E2ETests: XCTestCase { /// $feature = core /// $monitor_id = sdk_initialize_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - sdk_initialize: has a high average execution time" - /// $monitor_query = "avg(last_1d):avg:trace.sdk_initialize{env:instrumentation,resource_name:sdk_initialize,service:com.datadog.ios.nightly} > 0.016" + /// $monitor_query = "avg(last_1d):avg:trace.perf_measure{env:instrumentation,resource_name:sdk_initialize,service:com.datadog.ios.nightly} > 0.016" /// $monitor_threshold = 0.016 /// ``` // MARK: - Measuring Performance with APM - /// Measures time of execution for given `block` - sends it as a `Span` with a given name. - func measure(spanName: String, _ block: () -> Void) { + /// Measures time of execution for given `block` - sends it as a `"perf_measure"` `Span` with a given resource name. + func measure(resourceName: String, _ block: () -> Void) { let start = Date() block() let stop = Date() - Global.sharedTracer - .startRootSpan(operationName: spanName, startTime: start) - .finish(at: stop) + let performanceSpan = Global.sharedTracer.startRootSpan(operationName: "perf_measure", startTime: start) + performanceSpan.setTag(key: DDTags.resource, value: resourceName) + performanceSpan.finish(at: stop) } // MARK: - SDK Lifecycle diff --git a/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift b/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift index 193af5d39f..d3cd002123 100644 --- a/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggerBuilderE2ETests.swift @@ -21,7 +21,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_initialize_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_initialize: has a high average execution time" - /// $monitor_query = "avg(last_1d):avg:trace.logs_logger_initialize{env:instrumentation,resource_name:logs_logger_initialize,service:com.datadog.ios.nightly} > 0.016" + /// $monitor_query = "avg(last_1d):avg:trace.perf_measure{env:instrumentation,resource_name:logs_logger_initialize,service:com.datadog.ios.nightly} > 0.016" /// $monitor_threshold = 0.016 /// ``` @@ -36,7 +36,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly.custom @test_method_name:logs_logger_builder_set_service_name\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_set_SERVICE_NAME() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.set(serviceName: "com.datadog.ios.nightly.custom").build() } @@ -52,7 +52,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_set_logger_name @logger.name:custom_logger_name\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_set_LOGGER_NAME() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.set(loggerName: "custom_logger_name").build() } @@ -68,7 +68,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_send_network_info_enabled @network.client.reachability:*\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_SEND_NETWORK_INFO_enabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.sendNetworkInfo(true).build() } @@ -86,7 +86,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_logger_builder_SEND_NETWORK_INFO_disabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.sendNetworkInfo(false).build() } @@ -104,7 +104,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_send_logs_to_datadog_enabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_SEND_LOGS_TO_DATADOG_enabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.sendLogsToDatadog(true).build() } @@ -122,7 +122,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_logger_builder_SEND_LOGS_TO_DATADOG_disabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.sendLogsToDatadog(false).build() } @@ -138,7 +138,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_print_logs_to_console_enabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_PRINT_LOGS_TO_CONSOLE_enabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.printLogsToConsole(true, usingFormat: .random()).build() } @@ -154,7 +154,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_print_logs_to_console_disabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_PRINT_LOGS_TO_CONSOLE_disabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.printLogsToConsole(false, usingFormat: .random()).build() } @@ -172,7 +172,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_bundle_with_rum_enabled @application_id:* @session_id:*\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_BUNDLE_WITH_RUM_enabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.bundleWithRUM(true).build() } @@ -194,7 +194,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_logger_builder_BUNDLE_WITH_RUM_disabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.bundleWithRUM(false).build() } @@ -216,7 +216,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_bundle_with_trace_enabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_BUNDLE_WITH_TRACE_enabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.bundleWithTrace(true).build() } @@ -236,7 +236,7 @@ class LoggerBuilderE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_logger_builder_bundle_with_trace_disabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_logger_builder_BUNDLE_WITH_TRACE_disabled() { - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.bundleWithTrace(false).build() } diff --git a/Datadog/E2ETests/Logging/LoggerE2ETests.swift b/Datadog/E2ETests/Logging/LoggerE2ETests.swift index 29bff57e97..97a70306b6 100644 --- a/Datadog/E2ETests/Logging/LoggerE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggerE2ETests.swift @@ -35,10 +35,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_debug_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_debug_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_debug_log{env:instrumentation,resource_name:logs_logger_debug_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_debug_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_DEBUG_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.debug(.mockRandom(), attributes: DD.logAttributes()) } } @@ -57,10 +57,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_debug_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_debug_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_debug_log_with_error{env:instrumentation,resource_name:logs_logger_debug_log_with_error*,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_debug_log_with_error*,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_DEBUG_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.debug(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -79,10 +79,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_info_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_info_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_info_log{env:instrumentation,resource_name:logs_logger_info_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_info_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_INFO_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.info(.mockRandom(), attributes: DD.logAttributes()) } } @@ -101,10 +101,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_info_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_info_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_info_log_with_error{env:instrumentation,resource_name:logs_logger_info_log_with_error,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_info_log_with_error,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_INFO_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.info(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -123,10 +123,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_notice_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_notice_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_notice_log{env:instrumentation,resource_name:logs_logger_notice_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_notice_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_NOTICE_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.notice(.mockRandom(), attributes: DD.logAttributes()) } } @@ -145,10 +145,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_notice_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_notice_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_notice_log_with_error{env:instrumentation,resource_name:logs_logger_notice_log_with_error,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_notice_log_with_error,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_NOTICE_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.notice(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -167,10 +167,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_warn_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_warn_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_warn_log{env:instrumentation,resource_name:logs_logger_warn_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_warn_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_WARN_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.warn(.mockRandom(), attributes: DD.logAttributes()) } } @@ -189,10 +189,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_warn_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_warn_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_warn_log_with_error{env:instrumentation,resource_name:logs_logger_warn_log_with_error,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_warn_log_with_error,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_WARN_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.warn(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -211,10 +211,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_error_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_error_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_error_log{env:instrumentation,resource_name:logs_logger_error_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_error_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_ERROR_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.error(.mockRandom(), attributes: DD.logAttributes()) } } @@ -233,10 +233,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_error_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_error_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_error_log_with_error{env:instrumentation,resource_name:logs_logger_error_log_with_error,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_error_log_with_error,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_ERROR_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.error(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -255,10 +255,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_critical_log_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_critical_log: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_critical_log{env:instrumentation,resource_name:logs_logger_critical_log,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_critical_log,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_CRITICAL_log() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.critical(.mockRandom(), attributes: DD.logAttributes()) } } @@ -277,10 +277,10 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_critical_log_with_error_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_critical_log_with_error: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_critical_log_with_error{env:instrumentation,resource_name:logs_logger_critical_log_with_error,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_critical_log_with_error,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_CRITICAL_log_with_error() { - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.critical(.mockRandom(), error: ErrorMock(.mockRandom()), attributes: DD.logAttributes()) } } @@ -301,12 +301,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_string_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_string_attribute: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_string_attribute{env:instrumentation,resource_name:logs_logger_add_string_attribute,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_string_attribute,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_STRING_attribute() { let attribute = DD.specialStringAttribute() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.addAttribute(forKey: attribute.key, value: attribute.value) } @@ -327,12 +327,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_int_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_int_attribute: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_int_attribute{env:instrumentation,resource_name:logs_logger_add_int_attribute,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_int_attribute,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_INT_attribute() { let attribute = DD.specialIntAttribute() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.addAttribute(forKey: attribute.key, value: attribute.value) } @@ -353,12 +353,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_double_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_double_attribute: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_double_attribute{env:instrumentation,resource_name:logs_logger_add_double_attribute,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_double_attribute,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_DOUBLE_attribute() { let attribute = DD.specialDoubleAttribute() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.addAttribute(forKey: attribute.key, value: attribute.value) } @@ -379,12 +379,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_bool_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_bool_attribute: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_bool_attribute{env:instrumentation,resource_name:logs_logger_add_bool_attribute,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_bool_attribute,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_BOOL_attribute() { let attribute = DD.specialBoolAttribute() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.addAttribute(forKey: attribute.key, value: attribute.value) } @@ -407,7 +407,7 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_remove_attribute_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_attribute: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_attribute{env:instrumentation,resource_name:logs_logger_remove_attribute,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_remove_attribute,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_remove_attribute() { let possibleAttributes = [ @@ -420,7 +420,7 @@ class LoggerE2ETests: E2ETests { logger.addAttribute(forKey: randomAttribute.key, value: randomAttribute.value) - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.removeAttribute(forKey: randomAttribute.key) } @@ -443,12 +443,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_tag: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_tag{env:instrumentation,resource_name:logs_logger_add_tag,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_tag,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_tag() { let tag = DD.specialTag() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.addTag(withKey: tag.key, value: tag.value) } @@ -469,12 +469,12 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_add_already_formatted_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_add_already_formatted_tag: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_add_already_formatted_tag{env:instrumentation,resource_name:logs_logger_add_already_formatted_tag,service:com.datadog.ios.nightly} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,resource_name:logs_logger_add_already_formatted_tag,service:com.datadog.ios.nightly} > 0.024" /// ``` func test_logs_logger_add_already_formatted_tag() { let tag = DD.specialTag() - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.add(tag: "\(tag.key):\(tag.value)") } @@ -497,13 +497,13 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_remove_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_tag: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_tag{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_tag} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_tag} > 0.024" /// ``` func test_logs_logger_remove_tag() { let tag = DD.specialTag() logger.addTag(withKey: tag.key, value: tag.value) - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.removeTag(withKey: tag.key) } @@ -524,13 +524,13 @@ class LoggerE2ETests: E2ETests { /// $feature = logs /// $monitor_id = logs_logger_remove_already_formatted_tag_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - logs_logger_remove_already_formatted_tag: has a high average execution time" - /// $monitor_query = "avg(last_1d):p50:trace.logs_logger_remove_already_formatted_tag{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_already_formatted_tag} > 0.024" + /// $monitor_query = "avg(last_1d):p50:trace.perf_measure{env:instrumentation,service:com.datadog.ios.nightly,resource_name:logs_logger_remove_already_formatted_tag} > 0.024" /// ``` func test_logs_logger_remove_already_formatted_tag() { let tag = DD.specialTag() logger.add(tag: "\(tag.key):\(tag.value)") - measure(spanName: DD.PerfSpanName.fromCurrentMethodName()) { + measure(resourceName: DD.PerfSpanName.fromCurrentMethodName()) { logger.remove(tag: "\(tag.key):\(tag.value)") } diff --git a/Datadog/E2ETests/Logging/LoggingConfigurationE2ETests.swift b/Datadog/E2ETests/Logging/LoggingConfigurationE2ETests.swift index 53ed28b127..1850da2fae 100644 --- a/Datadog/E2ETests/Logging/LoggingConfigurationE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggingConfigurationE2ETests.swift @@ -29,7 +29,7 @@ class LoggingConfigurationE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_config_feature_enabled\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_config_feature_enabled() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK( trackingConsent: .granted, configuration: Datadog.Configuration.builderUsingE2EConfig() @@ -41,7 +41,7 @@ class LoggingConfigurationE2ETests: E2ETests { ) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } @@ -59,7 +59,7 @@ class LoggingConfigurationE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_feature_disabled() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK( trackingConsent: .granted, configuration: Datadog.Configuration.builderUsingE2EConfig() @@ -71,7 +71,7 @@ class LoggingConfigurationE2ETests: E2ETests { ) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } diff --git a/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift b/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift index beaa7031f1..4ac2c86290 100644 --- a/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift +++ b/Datadog/E2ETests/Logging/LoggingTrackingConsentE2ETests.swift @@ -26,7 +26,7 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $feature = logs /// $monitor_id = sdk_set_tracking_consent_performance /// $monitor_name = "[RUM] [iOS] Nightly Performance - sdk_set_tracking_consent: has a high average execution time" - /// $monitor_query = "avg(last_1d):avg:trace.sdk_set_tracking_consent{env:instrumentation,resource_name:sdk_set_tracking_consent,service:com.datadog.ios.nightly} > 0.016" + /// $monitor_query = "avg(last_1d):avg:trace.perf_measure{env:instrumentation,resource_name:sdk_set_tracking_consent,service:com.datadog.ios.nightly} > 0.016" /// $monitor_threshold = 0.016 /// ``` @@ -42,10 +42,10 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_config_consent_granted\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_config_consent_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .granted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } logger.sendRandomLog(with: DD.logAttributes()) @@ -63,10 +63,10 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_NOT_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .notGranted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } logger.sendRandomLog(with: DD.logAttributes()) @@ -84,10 +84,10 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_PENDING() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .pending) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } logger.sendRandomLog(with: DD.logAttributes()) @@ -106,13 +106,13 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_GRANTED_to_NOT_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .granted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .notGranted) } @@ -130,13 +130,13 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_GRANTED_to_PENDING() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .granted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .pending) } @@ -152,13 +152,13 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_config_consent_not_granted_to_granted\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_config_consent_NOT_GRANTED_to_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .notGranted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .granted) } @@ -176,13 +176,13 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_NOT_GRANTED_to_PENDING() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .notGranted) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .pending) } @@ -198,16 +198,16 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $monitor_query = "logs(\"service:com.datadog.ios.nightly @test_method_name:logs_config_consent_pending_to_granted\").index(\"*\").rollup(\"count\").last(\"1d\") < 1" /// ``` func test_logs_config_consent_PENDING_to_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .pending) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } logger.sendRandomLog(with: DD.logAttributes()) - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .granted) } } @@ -223,16 +223,16 @@ class LoggingTrackingConsentE2ETests: E2ETests { /// $notify_no_data = false /// ``` func test_logs_config_consent_PENDING_to_NOT_GRANTED() { - measure(spanName: DD.PerfSpanName.sdkInitialize) { + measure(resourceName: DD.PerfSpanName.sdkInitialize) { initializeSDK(trackingConsent: .pending) } - measure(spanName: DD.PerfSpanName.loggerInitialize) { + measure(resourceName: DD.PerfSpanName.loggerInitialize) { logger = Logger.builder.build() } logger.sendRandomLog(with: DD.logAttributes()) - measure(spanName: DD.PerfSpanName.setTrackingConsent) { + measure(resourceName: DD.PerfSpanName.setTrackingConsent) { Datadog.set(trackingConsent: .notGranted) } } From de4374202482c1eac25090aeb6a973faf6592072 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Mon, 20 Sep 2021 10:58:18 +0200 Subject: [PATCH 60/80] RUMM-1086 Long tasks are made explicit in UI Tests Even a simple button tap can take long (eg: 0.25s in my local) Long task threshold is increased to 1 second in UI Tests --- .../Example/Scenarios/RUM/RUMScenarios.swift | 2 +- .../RUM/RUMMobileVitalsScenarioTests.swift | 34 ++++++++++++++----- .../Matchers/RUMSessionMatcher.swift | 17 ++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift index 97d90b8939..e01cc4829e 100644 --- a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift +++ b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift @@ -129,7 +129,7 @@ final class RUMMobileVitalsScenario: TestScenario { _ = builder .trackUIKitRUMViews() .trackUIKitRUMActions() - .trackLongTasks() + .trackLongTasks(threshold: 2.5) .enableLogging(false) .enableTracing(false) } diff --git a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift index 05c7c1ae95..4b940a6262 100644 --- a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift +++ b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift @@ -30,30 +30,46 @@ class RUMMobileVitalsScenarioTests: IntegrationTests, RUMCommonAsserts { ) ) - // Wait for the app to settle down for 3 second - Thread.sleep(forTimeInterval: 3.0) - + // NOTE: RUMM-1086 even tapNoOpButton() can take up to 0.25sec in my local, + // therefore i used `threshold: 1.0` for long tasks in this scenario + app.tapNoOpButton() + app.tapBlockMainThreadButton() app.tapNoOpButton() app.tapBlockMainThreadButton() app.tapNoOpButton() // Get RUM Sessions with expected number of View visits let recordedRUMRequests = try rumServerSession.pullRecordedRequests(timeout: dataDeliveryTimeout) { requests in - let visitCount = try RUMSessionMatcher.singleSession(from: requests)?.viewVisits.count - return visitCount == 1 + let eventCount = try RUMSessionMatcher.singleSession(from: requests)?.longTaskEventMatchers.count + return eventCount == 2 } assertRUM(requests: recordedRUMRequests) let session = try XCTUnwrap(RUMSessionMatcher.singleSession(from: recordedRUMRequests)) - XCTAssertEqual(session.viewVisits[0].viewEvents.count, 2) + for event in session.viewVisits[0].viewEvents { + let longTask = try XCTUnwrap(event.view.longTask) + print(longTask.count) + } + + XCTAssertEqual(session.viewVisits[0].viewEvents.count, 5) let event1 = session.viewVisits[0].viewEvents[0].view let event2 = session.viewVisits[0].viewEvents[1].view + let event3 = session.viewVisits[0].viewEvents[2].view + let event4 = session.viewVisits[0].viewEvents[3].view + let event5 = session.viewVisits[0].viewEvents[4].view + + let longTask1 = try XCTUnwrap(event1.longTask) // start view + let longTask2 = try XCTUnwrap(event2.longTask) // no-op action + let longTask3 = try XCTUnwrap(event3.longTask) // block main thread + let longTask4 = try XCTUnwrap(event4.longTask) // no-op action + let longTask5 = try XCTUnwrap(event5.longTask) // block main thread - let longTask1 = try XCTUnwrap(event1.longTask) - let longTask2 = try XCTUnwrap(event2.longTask) - XCTAssertEqual(longTask1.count + 1, longTask2.count) + XCTAssertEqual(longTask1.count, longTask2.count) + XCTAssertEqual(longTask2.count + 1, longTask3.count) + XCTAssertEqual(longTask3.count, longTask4.count) + XCTAssertEqual(longTask4.count + 1, longTask5.count) let cpuTicksPerSec2 = try XCTUnwrap(event2.cpuTicksPerSecond) XCTAssertGreaterThan(cpuTicksPerSec2, 0.0) diff --git a/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift b/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift index 7a610b3959..f728c78219 100644 --- a/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift +++ b/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift @@ -78,6 +78,7 @@ internal class RUMSessionMatcher { let actionEventMatchers: [RUMEventMatcher] let resourceEventMatchers: [RUMEventMatcher] let errorEventMatchers: [RUMEventMatcher] + let longTaskEventMatchers: [RUMEventMatcher] private init(sessionEventMatchers: [RUMEventMatcher]) throws { // Sort events so they follow increasing time order @@ -97,6 +98,7 @@ internal class RUMSessionMatcher { self.actionEventMatchers = eventsMatchersByType["action"] ?? [] self.resourceEventMatchers = eventsMatchersByType["resource"] ?? [] self.errorEventMatchers = eventsMatchersByType["error"] ?? [] + self.longTaskEventMatchers = eventsMatchersByType["long_task"] ?? [] let viewEvents: [RUMViewEvent] = try viewEventMatchers.map { matcher in try matcher.model() } @@ -109,11 +111,15 @@ internal class RUMSessionMatcher { let errorEvents: [RUMErrorEvent] = try errorEventMatchers .map { matcher in try matcher.model() } + let longTaskEvents: [RUMLongTaskEvent] = try longTaskEventMatchers + .map { matcher in try matcher.model() } + // Validate each group of events individually try validate(rumViewEvents: viewEvents) try validate(rumActionEvents: actionEvents) try validate(rumResourceEvents: resourceEvents) try validate(rumErrorEvents: errorEvents) + try validate(rumLongTaskEvents: longTaskEvents) // Group RUMView events into ViewVisits: let uniqueViewIDs = Set(viewEvents.map { $0.view.id }) @@ -269,3 +275,14 @@ private func validate(rumErrorEvents: [RUMErrorEvent]) throws { } } } + +private func validate(rumLongTaskEvents: [RUMLongTaskEvent]) throws { + // All error events must use `session.plan` "lite" + try rumLongTaskEvents.forEach { longTaskEvent in + if longTaskEvent.dd.session?.plan != .plan1 { + throw RUMSessionConsistencyException( + description: "All RUM events must use session plan `1` (RUM Lite). Bad long task event: \(longTaskEvent)" + ) + } + } +} From b52814fd49b15088dbf20e0165c5c56365cad7f1 Mon Sep 17 00:00:00 2001 From: Ignacio Bonafonte Date: Mon, 20 Sep 2021 15:15:11 +0200 Subject: [PATCH 61/80] Update to version 0.9.4 of testing framework --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 94832db6dd..28833ea595 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ all: dependencies xcodeproj-httpservermock templates # The release version of `dd-sdk-swift-testing` to use for tests instrumentation. -DD_SDK_SWIFT_TESTING_VERSION = 0.9.4 +DD_SDK_SWIFT_TESTING_VERSION = 0.9.5 define DD_SDK_TESTING_XCCONFIG_CI FRAMEWORK_SEARCH_PATHS=$$(inherited) $$(SRCROOT)/../instrumented-tests/DatadogSDKTesting.xcframework/ios-arm64_x86_64-simulator/\n From 1910b0905bb83c5c06cfb883cac277def16891da Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Mon, 20 Sep 2021 16:11:58 +0200 Subject: [PATCH 62/80] RUMM-1086 PR comments + Objc APIs --- .../Example/Scenarios/RUM/RUMScenarios.swift | 2 +- Sources/Datadog/DatadogConfiguration.swift | 2 +- .../LongTasks/LongTaskObserver.swift | 4 +- .../RUM/RUMMonitor/Scopes/RUMViewScope.swift | 13 ++--- .../DatadogConfiguration+objc.swift | 18 +++++++ .../RUM/RUMMobileVitalsScenarioTests.swift | 52 +++++++++---------- .../DatadogConfigurationBuilderTests.swift | 2 +- .../RUM/RUMEvent/RUMEventSanitizerTests.swift | 3 ++ .../Datadog/RUMMonitorTests.swift | 16 +++++- .../DatadogObjc/DDConfigurationTests.swift | 17 ++++++ .../ObjcAPITests/DDConfiguration+apiTests.m | 5 ++ .../Matchers/RUMSessionMatcher.swift | 13 +++++ 12 files changed, 107 insertions(+), 40 deletions(-) diff --git a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift index e01cc4829e..217303324e 100644 --- a/Datadog/Example/Scenarios/RUM/RUMScenarios.swift +++ b/Datadog/Example/Scenarios/RUM/RUMScenarios.swift @@ -129,7 +129,7 @@ final class RUMMobileVitalsScenario: TestScenario { _ = builder .trackUIKitRUMViews() .trackUIKitRUMActions() - .trackLongTasks(threshold: 2.5) + .trackRUMLongTasks(threshold: 2.5) .enableLogging(false) .enableTracing(false) } diff --git a/Sources/Datadog/DatadogConfiguration.swift b/Sources/Datadog/DatadogConfiguration.swift index a1b3eb1779..e01437b920 100644 --- a/Sources/Datadog/DatadogConfiguration.swift +++ b/Sources/Datadog/DatadogConfiguration.swift @@ -599,7 +599,7 @@ extension Datadog { /// Enable long operations on the main thread to be tracked automatically. /// Any long running operation on the main thread will appear as Long Tasks in Datadog RUM Explorer. /// - Parameter threshold: the threshold in seconds above which a task running on the Main thread is considered as a long task (default 0.1 second) - public func trackLongTasks(threshold: TimeInterval = 0.1) -> Builder { + public func trackRUMLongTasks(threshold: TimeInterval = 0.1) -> Builder { configuration.rumLongTaskDurationThreshold = threshold return self } diff --git a/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift b/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift index 2f726301f2..ba471f3e82 100644 --- a/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift +++ b/Sources/Datadog/RUM/AutoInstrumentation/LongTasks/LongTaskObserver.swift @@ -7,8 +7,8 @@ import Foundation internal class LongTaskObserver { - let longTaskDurationThreshold: TimeInterval - let dateProvider: DateProvider + private let longTaskDurationThreshold: TimeInterval + private let dateProvider: DateProvider private var observer_begin: CFRunLoopObserver? private var observer_end: CFRunLoopObserver? diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift index 6a6751381f..6f200bd369 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift @@ -184,16 +184,13 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { } case let command as RUMAddLongTaskCommand where isActiveView: - longTasksCount += 1 - if command.duration.toInt64Nanoseconds > Constants.frozenFrameThresholdInNs { - frozenFramesCount += 1 - } - if sendLongTaskEvent(on: command) { + longTasksCount += 1 + if command.duration.toInt64Nanoseconds > Constants.frozenFrameThresholdInNs { + frozenFramesCount += 1 + } + needsViewUpdate = true - } else { - longTasksCount -= 1 - frozenFramesCount -= 1 } default: diff --git a/Sources/DatadogObjc/DatadogConfiguration+objc.swift b/Sources/DatadogObjc/DatadogConfiguration+objc.swift index 56f4247969..a51b468486 100644 --- a/Sources/DatadogObjc/DatadogConfiguration+objc.swift +++ b/Sources/DatadogObjc/DatadogConfiguration+objc.swift @@ -292,6 +292,16 @@ public class DDConfigurationBuilder: NSObject { _ = sdkBuilder.trackUIKitRUMActions(using: predicateBridge) } + @objc + public func trackRUMLongTasks() { + _ = sdkBuilder.trackRUMLongTasks() + } + + @objc + public func trackRUMLongTasks(threshold: TimeInterval) { + _ = sdkBuilder.trackRUMLongTasks(threshold: threshold) + } + @objc public func setRUMViewEventMapper(_ mapper: @escaping (DDRUMViewEvent) -> DDRUMViewEvent) { _ = sdkBuilder.setRUMViewEventMapper { swiftEvent in @@ -324,6 +334,14 @@ public class DDConfigurationBuilder: NSObject { } } + @objc + public func setRUMLongTaskEventMapper(_ mapper: @escaping (DDRUMLongTaskEvent) -> DDRUMLongTaskEvent?) { + _ = sdkBuilder.setRUMLongTaskEventMapper { swiftEvent in + let objcEvent = DDRUMLongTaskEvent(swiftModel: swiftEvent) + return mapper(objcEvent)?.swiftModel + } + } + @objc public func set(batchSize: DDBatchSize) { _ = sdkBuilder.set(batchSize: batchSize.swiftType) diff --git a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift index 4b940a6262..05fb4581db 100644 --- a/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift +++ b/Tests/DatadogIntegrationTests/Scenarios/RUM/RUMMobileVitalsScenarioTests.swift @@ -31,9 +31,9 @@ class RUMMobileVitalsScenarioTests: IntegrationTests, RUMCommonAsserts { ) // NOTE: RUMM-1086 even tapNoOpButton() can take up to 0.25sec in my local, - // therefore i used `threshold: 1.0` for long tasks in this scenario + // therefore i used `threshold: 2.5` for long tasks in this scenario app.tapNoOpButton() - app.tapBlockMainThreadButton() + app.tapBlockMainThreadButton() // block main thread for 3 seconds app.tapNoOpButton() app.tapBlockMainThreadButton() app.tapNoOpButton() @@ -48,33 +48,33 @@ class RUMMobileVitalsScenarioTests: IntegrationTests, RUMCommonAsserts { let session = try XCTUnwrap(RUMSessionMatcher.singleSession(from: recordedRUMRequests)) - for event in session.viewVisits[0].viewEvents { - let longTask = try XCTUnwrap(event.view.longTask) - print(longTask.count) - } - XCTAssertEqual(session.viewVisits[0].viewEvents.count, 5) - let event1 = session.viewVisits[0].viewEvents[0].view - let event2 = session.viewVisits[0].viewEvents[1].view - let event3 = session.viewVisits[0].viewEvents[2].view - let event4 = session.viewVisits[0].viewEvents[3].view - let event5 = session.viewVisits[0].viewEvents[4].view - - let longTask1 = try XCTUnwrap(event1.longTask) // start view - let longTask2 = try XCTUnwrap(event2.longTask) // no-op action - let longTask3 = try XCTUnwrap(event3.longTask) // block main thread - let longTask4 = try XCTUnwrap(event4.longTask) // no-op action - let longTask5 = try XCTUnwrap(event5.longTask) // block main thread - - XCTAssertEqual(longTask1.count, longTask2.count) - XCTAssertEqual(longTask2.count + 1, longTask3.count) - XCTAssertEqual(longTask3.count, longTask4.count) - XCTAssertEqual(longTask4.count + 1, longTask5.count) - - let cpuTicksPerSec2 = try XCTUnwrap(event2.cpuTicksPerSecond) + + let viewLongTask1 = try XCTUnwrap(session.viewVisits[0].viewEvents[0].view.longTask) // start view + let viewLongTask2 = try XCTUnwrap(session.viewVisits[0].viewEvents[1].view.longTask) // no-op action + let viewLongTask3 = try XCTUnwrap(session.viewVisits[0].viewEvents[2].view.longTask) // block main thread + let viewLongTask4 = try XCTUnwrap(session.viewVisits[0].viewEvents[3].view.longTask) // no-op action + let viewLongTask5 = try XCTUnwrap(session.viewVisits[0].viewEvents[4].view.longTask) // block main thread + + XCTAssertEqual(viewLongTask1.count, viewLongTask2.count, "Add action event should NOT increment long task count") + XCTAssertEqual(viewLongTask2.count + 1, viewLongTask3.count, "Block main thread button should increment long task count") + XCTAssertEqual(viewLongTask3.count, viewLongTask4.count, "Add action event should NOT increment long task count") + XCTAssertEqual(viewLongTask4.count + 1, viewLongTask5.count, "Block main thread button should increment long task count") + + let viewEvent = session.viewVisits[0].viewEvents[2].view + + let cpuTicksPerSec2 = try XCTUnwrap(viewEvent.cpuTicksPerSecond) XCTAssertGreaterThan(cpuTicksPerSec2, 0.0) - let fps2 = try XCTUnwrap(event2.refreshRateAverage) + let fps2 = try XCTUnwrap(viewEvent.refreshRateAverage) XCTAssertGreaterThan(fps2, 0.0) + + let longTaskEvents = session.viewVisits[0].longTaskEvents + XCTAssertEqual(longTaskEvents.count, 2) + + let longTask1 = longTaskEvents[0] + XCTAssertGreaterThan(longTask1.longTask.duration, 3_000_000_000) + let longTask2 = longTaskEvents[1] + XCTAssertGreaterThan(longTask2.longTask.duration, 3_000_000_000) } } diff --git a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift index 5cdc62f711..16c85d6e7b 100644 --- a/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift +++ b/Tests/DatadogTests/Datadog/DatadogConfigurationBuilderTests.swift @@ -87,7 +87,7 @@ class DatadogConfigurationBuilderTests: XCTestCase { .trackURLSession(firstPartyHosts: ["example.com"]) .trackUIKitRUMViews(using: UIKitRUMViewsPredicateMock()) .trackUIKitRUMActions(using: UIKitRUMUserActionsPredicateMock()) - .trackLongTasks(threshold: 100.0) + .trackRUMLongTasks(threshold: 100.0) .trackBackgroundEvents(false) .setRUMViewEventMapper { _ in mockRUMViewEvent } .setRUMErrorEventMapper { _ in mockRUMErrorEvent } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift index 9c7dde8080..ae7fdf672e 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMEvent/RUMEventSanitizerTests.swift @@ -12,6 +12,7 @@ class RUMEventSanitizerTests: XCTestCase { private let resourceEvent: RUMResourceEvent = .mockRandom() private let actionEvent: RUMActionEvent = .mockRandom() private let errorEvent: RUMErrorEvent = .mockRandom() + private let longTaskEvent: RUMLongTaskEvent = .mockRandom() func testWhenAttributeNameExceeds10NestedLevels_itIsEscapedByUnderscore() { func test(event: Event) where Event: RUMSanitizableEvent { @@ -81,6 +82,7 @@ class RUMEventSanitizerTests: XCTestCase { test(event: resourceEvent) test(event: actionEvent) test(event: errorEvent) + test(event: longTaskEvent) } func testWhenNumberOfAttributesExceedsLimit_itDropsExtraOnes() { @@ -121,6 +123,7 @@ class RUMEventSanitizerTests: XCTestCase { test(event: resourceEvent) test(event: actionEvent) test(event: errorEvent) + test(event: longTaskEvent) } // MARK: - Private diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index 325e2b6f96..0f10563533 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -958,6 +958,11 @@ class RUMMonitorTests: XCTestCase { var errorEvent = errorEvent errorEvent.error.message = "Modified error message" return errorEvent + }, + longTaskEventMapper: { longTaskEvent in + var mutableLongTaskEvent = longTaskEvent + mutableLongTaskEvent.view.name = "ModifiedLongTaskViewName" + return mutableLongTaskEvent } ), dependencies: .mockWith( @@ -974,6 +979,9 @@ class RUMMonitorTests: XCTestCase { monitor.addUserAction(type: .tap, name: "Original tap action name") monitor.addError(message: "Original error message") + let cmdSubscriber = try XCTUnwrap(monitor as? RUMMonitor) + cmdSubscriber.process(command: RUMAddLongTaskCommand(time: Date(), attributes: [:], duration: 1.0)) + let rumEventMatchers = try RUMFeature.waitAndReturnRUMEventMatchers(count: 5) let sessions = try RUMSessionMatcher.groupMatchersBySessions(rumEventMatchers) @@ -990,6 +998,7 @@ class RUMMonitorTests: XCTestCase { XCTAssertEqual(session.viewVisits[0].actionEvents[0].action.target?.name, "Modified tap action name") XCTAssertEqual(session.viewVisits[0].errorEvents.count, 1) XCTAssertEqual(session.viewVisits[0].errorEvents[0].error.message, "Modified error message") + XCTAssertEqual(session.viewVisits[0].longTaskEvents[0].view.name, "ModifiedLongTaskViewName") } func testDroppingEventsBeforeTheyGetSent() throws { @@ -1000,7 +1009,8 @@ class RUMMonitorTests: XCTestCase { actionEventMapper: { event in return event.action.type == .applicationStart ? event : nil }, - errorEventMapper: { _ in nil } + errorEventMapper: { _ in nil }, + longTaskEventMapper: { _ in nil } ) ) defer { RUMFeature.instance?.deinitialize() } @@ -1013,6 +1023,9 @@ class RUMMonitorTests: XCTestCase { monitor.addUserAction(type: .tap, name: .mockAny()) monitor.addError(message: .mockAny()) + let cmdSubscriber = try XCTUnwrap(monitor as? RUMMonitor) + cmdSubscriber.process(command: RUMAddLongTaskCommand(time: Date(), attributes: [:], duration: 1.0)) + let rumEventMatchers = try RUMFeature.waitAndReturnRUMEventMatchers(count: 2) let sessions = try RUMSessionMatcher.groupMatchersBySessions(rumEventMatchers) @@ -1027,6 +1040,7 @@ class RUMMonitorTests: XCTestCase { XCTAssertEqual(session.viewVisits[0].resourceEvents.count, 0) XCTAssertEqual(session.viewVisits[0].actionEvents.count, 1) XCTAssertEqual(session.viewVisits[0].errorEvents.count, 0) + XCTAssertEqual(session.viewVisits[0].longTaskEvents.count, 0) } // MARK: - Integration with Crash Reporting diff --git a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift index a7e9524de2..a036aa5a87 100644 --- a/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift +++ b/Tests/DatadogTests/DatadogObjc/DDConfigurationTests.swift @@ -136,6 +136,9 @@ class DDConfigurationTests: XCTestCase { objcBuilder.trackUIKitRUMActions() XCTAssertTrue(objcBuilder.build().sdkConfiguration.rumUIKitUserActionsPredicate is DefaultUIKitRUMUserActionsPredicate) + objcBuilder.trackRUMLongTasks(threshold: 999.0) + XCTAssertEqual(objcBuilder.build().sdkConfiguration.rumLongTaskDurationThreshold, 999.0) + objcBuilder.trackUIKitRUMViews() XCTAssertTrue(objcBuilder.build().sdkConfiguration.rumUIKitViewsPredicate is DefaultUIKitRUMViewsPredicate) @@ -168,6 +171,9 @@ class DDConfigurationTests: XCTestCase { objcBuilder.setRUMErrorEventMapper { _ in nil } XCTAssertNotNil(objcBuilder.build().sdkConfiguration.rumErrorEventMapper) + objcBuilder.setRUMLongTaskEventMapper { _ in nil } + XCTAssertNotNil(objcBuilder.build().sdkConfiguration.rumLongTaskEventMapper) + objcBuilder.set(batchSize: .small) XCTAssertEqual(objcBuilder.build().sdkConfiguration.batchSize, .small) @@ -203,6 +209,7 @@ class DDConfigurationTests: XCTestCase { let swiftResourceEvent: RUMResourceEvent = .mockRandom() let swiftActionEvent: RUMActionEvent = .mockRandom() let swiftErrorEvent: RUMErrorEvent = .mockRandom() + let swiftLongTaskEvent: RUMLongTaskEvent = .mockRandom() objcBuilder.setRUMViewEventMapper { objcViewEvent in XCTAssertEqual(objcViewEvent.swiftModel, swiftViewEvent) @@ -232,12 +239,19 @@ class DDConfigurationTests: XCTestCase { return objcErrorEvent } + objcBuilder.setRUMLongTaskEventMapper { objcLongTaskEvent in + XCTAssertEqual(objcLongTaskEvent.swiftModel, swiftLongTaskEvent) + objcLongTaskEvent.view.url = "redacted view.url" + return objcLongTaskEvent + } + let configuration = objcBuilder.build().sdkConfiguration let redactedSwiftViewEvent = configuration.rumViewEventMapper?(swiftViewEvent) let redactedSwiftResourceEvent = configuration.rumResourceEventMapper?(swiftResourceEvent) let redactedSwiftActionEvent = configuration.rumActionEventMapper?(swiftActionEvent) let redactedSwiftErrorEvent = configuration.rumErrorEventMapper?(swiftErrorEvent) + let redactedSwiftLongTaskEvent = configuration.rumLongTaskEventMapper?(swiftLongTaskEvent) XCTAssertEqual(redactedSwiftViewEvent?.view.url, "redacted view.url") XCTAssertEqual(redactedSwiftResourceEvent?.view.url, "redacted view.url") @@ -247,6 +261,7 @@ class DDConfigurationTests: XCTestCase { XCTAssertEqual(redactedSwiftErrorEvent?.view.url, "redacted view.url") XCTAssertEqual(redactedSwiftErrorEvent?.error.message, "redacted error.message") XCTAssertEqual(redactedSwiftErrorEvent?.error.resource?.url, "redacted error.resource.url") + XCTAssertEqual(redactedSwiftLongTaskEvent?.view.url, "redacted view.url") } func testDroppingRUMEvents() { @@ -259,12 +274,14 @@ class DDConfigurationTests: XCTestCase { objcBuilder.setRUMResourceEventMapper { _ in nil } objcBuilder.setRUMActionEventMapper { _ in nil } objcBuilder.setRUMErrorEventMapper { _ in nil } + objcBuilder.setRUMLongTaskEventMapper { _ in nil } let configuration = objcBuilder.build().sdkConfiguration XCTAssertNil(configuration.rumResourceEventMapper?(.mockRandom())) XCTAssertNil(configuration.rumActionEventMapper?(.mockRandom())) XCTAssertNil(configuration.rumErrorEventMapper?(.mockRandom())) + XCTAssertNil(configuration.rumLongTaskEventMapper?(.mockRandom())) } func testDeprecatedTrackUIActions() { diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m index c01d2e47a6..9dbc42301f 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDConfiguration+apiTests.m @@ -92,6 +92,8 @@ - (void)testDDConfigurationBuilderAPI { viewEvent.view.url = @""; return viewEvent; }]; + [builder trackRUMLongTasks]; + [builder trackRUMLongTasksWithThreshold:10.0]; [builder setRUMResourceEventMapper:^DDRUMResourceEvent * _Nullable(DDRUMResourceEvent * _Nonnull resourceEvent) { resourceEvent.resource.url = @""; return resourceEvent; @@ -102,6 +104,9 @@ - (void)testDDConfigurationBuilderAPI { [builder setRUMErrorEventMapper:^DDRUMErrorEvent * _Nullable(DDRUMErrorEvent * _Nonnull errorEvent) { return nil; }]; + [builder setRUMLongTaskEventMapper:^DDRUMLongTaskEvent * _Nullable(DDRUMLongTaskEvent * _Nonnull longTaskEvent) { + return nil; + }]; [builder setWithBatchSize:DDBatchSizeMedium]; [builder setWithUploadFrequency:DDUploadFrequencyAverage]; [builder setWithAdditionalConfiguration:@{}]; diff --git a/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift b/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift index f728c78219..ec1622f9d6 100644 --- a/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift +++ b/Tests/DatadogTests/Matchers/RUMSessionMatcher.swift @@ -68,6 +68,9 @@ internal class RUMSessionMatcher { /// `RUMError` events tracked during this visit. fileprivate(set) var errorEvents: [RUMErrorEvent] = [] + + /// `RUMLongTask` events tracked during this visit. + fileprivate(set) var longTaskEvents: [RUMLongTaskEvent] = [] } /// An array of view visits tracked during this RUM Session. @@ -184,6 +187,16 @@ internal class RUMSessionMatcher { } } + try longTaskEvents.forEach { rumEvent in + if let visit = visitsByViewID[rumEvent.view.id] { + visit.longTaskEvents.append(rumEvent) + } else { + throw RUMSessionConsistencyException( + description: "Cannot link RUM Event: \(rumEvent) to `RUMSessionMatcher.ViewVisit` by `view.id`." + ) + } + } + // Sort visits by time let visitsEventOrderedByTime = visits.sorted { firstVisit, secondVisit in let firstVisitTime = firstVisit.viewEvents[0].date From e6a4802c5d1fbd39d3427edd4c0ae46e324b71d0 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Tue, 21 Sep 2021 16:43:33 +0200 Subject: [PATCH 63/80] RUMM-1588 Add corresponding code to each monitor definition --- .../monitors-gen/templates/monitor-apm.tf.src | 6 ++ .../templates/monitor-logs.tf.src | 6 ++ .../nightly-e2e-tests/src/main_tf_renderer.py | 8 ++ .../nightly-e2e-tests/src/test_file_parser.py | 101 ++++++++++++++---- .../tests/test_parsing_test_file.py | 35 +++++- .../tests/test_rendering_monitor_template.py | 32 ++++++ 6 files changed, 165 insertions(+), 23 deletions(-) diff --git a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src index 8ecd60550c..6b442975e4 100644 --- a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src +++ b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-apm.tf.src @@ -5,6 +5,12 @@ resource "datadog_monitor" ${{monitor_id}} { message = < diff --git a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-logs.tf.src b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-logs.tf.src index 935dae3164..cef82c1047 100644 --- a/tools/nightly-e2e-tests/monitors-gen/templates/monitor-logs.tf.src +++ b/tools/nightly-e2e-tests/monitors-gen/templates/monitor-logs.tf.src @@ -5,6 +5,12 @@ resource "datadog_monitor" ${{monitor_id}} { message = < diff --git a/tools/nightly-e2e-tests/src/main_tf_renderer.py b/tools/nightly-e2e-tests/src/main_tf_renderer.py index b06aa6f179..77101462cf 100644 --- a/tools/nightly-e2e-tests/src/main_tf_renderer.py +++ b/tools/nightly-e2e-tests/src/main_tf_renderer.py @@ -73,6 +73,7 @@ def render(self, monitor: MonitorConfiguration) -> str: result = '' result += self.template_content result = MonitorTemplate.render_template_variables(template=result, monitor=monitor) + result = MonitorTemplate.render_monitor_code(template=result, monitor=monitor) return result @staticmethod @@ -131,3 +132,10 @@ def render_template_variables(template: str, monitor: MonitorConfiguration) -> s ) return '\n'.join(rendered_lines) + + @staticmethod + def render_monitor_code(template: str, monitor: MonitorConfiguration) -> str: + """ + Replaces '## MONITOR_CODE ##' anchor in the template with the code associated to this monitor. + """ + return template.replace("## MONITOR_CODE ##", monitor.code) diff --git a/tools/nightly-e2e-tests/src/test_file_parser.py b/tools/nightly-e2e-tests/src/test_file_parser.py index d5b2007318..5c97bfc191 100644 --- a/tools/nightly-e2e-tests/src/test_file_parser.py +++ b/tools/nightly-e2e-tests/src/test_file_parser.py @@ -25,6 +25,7 @@ class MonitorConfiguration: type: str # a type of monitor (allowed values: 'apm' | 'logs'), used to select monitor template variables: [MonitorVariable] # list of monitor variables code_reference: CodeReference + code: str = '' # monitor code (monitor definition from comment and the entire test method declaration) @dataclass @@ -32,6 +33,7 @@ class TestMethod: method_name: str # the name of this test method monitors: [MonitorConfiguration] # monitors defined in method comment code_reference: CodeReference + code: str # the entire test method declaration code @dataclass() @@ -47,38 +49,64 @@ def read_test_file(path: str): """ with open(path, 'r') as file: comment_regex = r'^[\t ]*(\/\/\/.*)' # e.g. ' /// Sample comment' - method_signature_regex = r'^[\s ]+func (test\w*)\(\)( throws)? {' # e.g. ' func test_sample() throws {' + method_signature_regex = r'^[\t ]+func (test\w*)\(\)( throws)? {' # e.g. ' func test_sample() throws {' test_methods: [TestMethod] = [] independent_monitors: [MonitorConfiguration] = [] - comment_lines_buffer: [(int, str)] = [] + comment_lines_buffer: [(int, str, str)] = [] # (line number, comment with no indent, original line) - for line_no, line_text in enumerate(file.readlines()): + lines_in_file = file.readlines() + + for line_no, line_text in enumerate(lines_in_file): if comment_match := re.match(comment_regex, line_text): # matched comment `///` - comment_lines_buffer.append((line_no, comment_match.groups()[0])) + comment_lines_buffer.append((line_no, comment_match.groups()[0], line_text)) elif method_signature_match := re.match(method_signature_regex, line_text): # matched test method signature method_name = method_signature_match.groups()[0] - method = TestMethod( - method_name=method_name, - monitors=read_monitor_configuration( + + method_code_reference = CodeReference( + file_path=path, + line_no=line_no + 1, + line_text=line_text + ) + + with linter_context(code_reference=method_code_reference): + method_monitors = read_monitors_configuration( # read monitors defined in this method comment comment_lines=comment_lines_buffer, file_path=path - ), - code_reference=CodeReference( - file_path=path, - line_no=line_no + 1, - line_text=line_text ) - ) + method_code = read_method_body( # read method body + lines_in_file=lines_in_file, + method_signature_line_no=line_no + ) + + # Link code declaration to each monitor in this test method: + for method_monitor in method_monitors: + comment_lines = list(map(lambda buffered_line: buffered_line[2], comment_lines_buffer)) + method_monitor.code = ''.join(comment_lines) + method_code + + method = TestMethod( + method_name=method_name, + monitors=method_monitors, + code_reference=method_code_reference, + code=method_code + ) + test_methods.append(method) comment_lines_buffer = [] else: # matched some other line in the file - # Check if buffered comments define any additional monitors: - additional_monitors = read_monitor_configuration( + # Check if buffered comments define any additional monitors (independent monitors defined + # in the test file, but not linked to any test method): + additional_monitors = read_monitors_configuration( comment_lines=comment_lines_buffer, file_path=path ) + + # Link code declaration to each additional monitor: + for additional_monitor in additional_monitors: + comment_lines = list(map(lambda buffered_line: buffered_line[2], comment_lines_buffer)) + additional_monitor.code = ''.join(comment_lines) + # Add to the list of independent monitors for this file: independent_monitors += additional_monitors @@ -93,11 +121,11 @@ def read_test_file(path: str): return None -def read_monitor_configuration(comment_lines: [(int, str)], file_path: str) -> [MonitorConfiguration]: +def read_monitors_configuration(comment_lines: [(int, str, str)], file_path: str) -> [MonitorConfiguration]: """ - Parses method comment lines and produces one or more `MonitorConfiguration` objects. + Parses comment lines and returns zero to many `MonitorConfiguration` objects. - The expected method comment has following structure: + The expected monitor comment has following structure: /// ... anything before /// ```logs @@ -113,7 +141,7 @@ def read_monitor_configuration(comment_lines: [(int, str)], file_path: str) -> [ The "```" token indicates the beginning and end of the monitor definition. There can be more than one monitor defined in each comment. - :return: returns list of `MonitorConfiguration` objects (0 to many) recognized in the method comment. + :return: returns list of `MonitorConfiguration` objects (0 to many) recognized in the comment. """ monitor_region_start_regex = r'^\/\/\/[\s ]+```([a-zA-Z0-9]+)[\s ]*$' # e.g. '/// ```apm' monitor_region_end_regex = r'^\/\/\/[\s ]+```[\s ]*$' # e.g. '/// ```' @@ -124,7 +152,7 @@ def read_monitor_configuration(comment_lines: [(int, str)], file_path: str) -> [ monitors: [MonitorConfiguration] = [] variables_buffer: [MonitorVariable] = [] - for line_no, line_text in comment_lines: + for line_no, line_text, _ in comment_lines: comment_line_code_reference = CodeReference( file_path=file_path, line_no=(line_no + 1), @@ -198,3 +226,36 @@ def read_variable(line_text: str, comment_line_code_reference: CodeReference): with linter_context(code_reference=comment_line_code_reference): Linter.shared.emit_error('Incorrect variable definitions - variable must follow `$name = value` syntax.') return None + + +def read_method_body(lines_in_file: [str], method_signature_line_no: int) -> str: + """ + Parses method body. + + It starts from `method_signature_line` and accepts lines until all opening brackets "{" are matched + with enclosing "}" brackets. + + :return: the body of test method, including its signature and enclosing `}` bracket. + """ + + method_body = '' + line_no = method_signature_line_no + brackets_matched = 0 + + while line_no < len(lines_in_file): + line_text = lines_in_file[line_no] + brackets_matched += line_text.count('{') + brackets_matched -= line_text.count('}') + + method_body += line_text + + if brackets_matched == 0: + break + else: + line_no += 1 + + if line_no == (len(lines_in_file) - 1): + Linter.shared.emit_warning('E2E monitors parser cannot read this method body. Make sure that all opening' + ' brackets "{" are matched with their closing bracket "}".') + + return method_body diff --git a/tools/nightly-e2e-tests/tests/test_parsing_test_file.py b/tools/nightly-e2e-tests/tests/test_parsing_test_file.py index dcf3a4d01c..a68514aeb1 100644 --- a/tools/nightly-e2e-tests/tests/test_parsing_test_file.py +++ b/tools/nightly-e2e-tests/tests/test_parsing_test_file.py @@ -55,9 +55,21 @@ def test_it_reads_monitor_definition_from_test_method(self): ) ], code_reference=CodeReference( - file_path=file.name, - line_no=3, - line_text='/// ```' + file_path=file.name, + line_no=3, + line_text='/// ```' + ), + code='\n'.join( + [ + ' /// Monitor definition assigned to the test method:', + ' /// ```apm', + ' /// $foo = ', + ' /// $bar = ', + ' /// ```', + ' func test_method_name() {', + ' }', + '' + ] ) ) ], @@ -65,6 +77,13 @@ def test_it_reads_monitor_definition_from_test_method(self): file_path=file.name, line_no=7, line_text=' func test_method_name() {\n' + ), + code='\n'.join( + [ + ' func test_method_name() {', + ' }', + '' + ] ) ) ], @@ -116,6 +135,16 @@ class Foo { file_path=file.name, line_no=4, line_text='/// ```' + ), + code='\n'.join( + [ + ' /// Monitor definition not assigned to any test method:', + ' /// ```apm', + ' /// $foo = ', + ' /// $bar = ', + ' /// ```', + '' + ] ) ) ] diff --git a/tools/nightly-e2e-tests/tests/test_rendering_monitor_template.py b/tools/nightly-e2e-tests/tests/test_rendering_monitor_template.py index 3fe9155328..3d07ff2e6b 100644 --- a/tools/nightly-e2e-tests/tests/test_rendering_monitor_template.py +++ b/tools/nightly-e2e-tests/tests/test_rendering_monitor_template.py @@ -110,11 +110,43 @@ def test_when_mandatory_variable_is_not_specified_it_fails(self): message='Variable $argument_with_no_default is required, but not defined for this monitor.' ) + def test_it_renders_monitor_code(self): + template = MonitorTemplate( + ''' + some text before + ## MONITOR_CODE ## + some text after + ''' + ) + + monitor_code = random_multiline_string(length=256) + + monitor = MonitorConfiguration( + type='', + variables=[], + code_reference=any_code_reference(), + code=monitor_code + ) + + rendered_template = template.render(monitor=monitor) + expected_template = f''' + some text before + {monitor_code} + some text after + ''' + + self.assertEqual(expected_template, rendered_template) + def random_string(length: int = 32): characters_set = string.ascii_letters + string.digits + string.punctuation + ' \t' return ''.join((random.choice(characters_set) for i in range(length))) +def random_multiline_string(length: int = 32): + characters_set = string.ascii_letters + string.digits + string.punctuation + ' \t' + '\n' + return ''.join((random.choice(characters_set) for i in range(length))) + + def any_code_reference(): return CodeReference(file_path='', line_no=0, line_text='') From 74d4aa75eb8dbb78edf50fa3a142058f771f2f6e Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Wed, 22 Sep 2021 14:14:59 +0200 Subject: [PATCH 64/80] Bumped version to 1.7.0 --- DatadogSDK.podspec | 2 +- DatadogSDKAlamofireExtension.podspec | 2 +- DatadogSDKCrashReporting.podspec | 4 ++-- DatadogSDKObjc.podspec | 4 ++-- Sources/Datadog/Versioning.swift | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DatadogSDK.podspec b/DatadogSDK.podspec index 6ec8682a49..f6adf59c4c 100644 --- a/DatadogSDK.podspec +++ b/DatadogSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDK" s.module_name = "Datadog" - s.version = "1.7.0-beta4" + s.version = "1.7.0" s.summary = "Official Datadog Swift SDK for iOS." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogSDKAlamofireExtension.podspec b/DatadogSDKAlamofireExtension.podspec index 9b0ed0ea80..c186d16002 100644 --- a/DatadogSDKAlamofireExtension.podspec +++ b/DatadogSDKAlamofireExtension.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKAlamofireExtension" s.module_name = "DatadogAlamofireExtension" - s.version = "1.7.0-beta4" + s.version = "1.7.0" s.summary = "An Official Extensions of Datadog Swift SDK for Alamofire." s.homepage = "https://www.datadoghq.com" diff --git a/DatadogSDKCrashReporting.podspec b/DatadogSDKCrashReporting.podspec index 915fde6c3b..6204f88918 100644 --- a/DatadogSDKCrashReporting.podspec +++ b/DatadogSDKCrashReporting.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKCrashReporting" s.module_name = "DatadogCrashReporting" - s.version = "1.7.0-beta4" + s.version = "1.7.0" s.summary = "Official Datadog Crash Reporting SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -20,6 +20,6 @@ Pod::Spec.new do |s| s.static_framework = true s.source_files = "Sources/DatadogCrashReporting/**/*.swift" - s.dependency 'DatadogSDK', '1.7.0-beta4' + s.dependency 'DatadogSDK', '1.7.0' s.dependency 'PLCrashReporter', '~> 1.10.0' end diff --git a/DatadogSDKObjc.podspec b/DatadogSDKObjc.podspec index 74de31f592..c52e2ff374 100644 --- a/DatadogSDKObjc.podspec +++ b/DatadogSDKObjc.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "DatadogSDKObjc" s.module_name = "DatadogObjc" - s.version = "1.7.0-beta4" + s.version = "1.7.0" s.summary = "Official Datadog Objective-C SDK for iOS." s.homepage = "https://www.datadoghq.com" @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/DataDog/dd-sdk-ios.git', :tag => s.version.to_s } s.source_files = "Sources/DatadogObjc/**/*.swift" - s.dependency 'DatadogSDK', '1.7.0-beta4' + s.dependency 'DatadogSDK', '1.7.0' end diff --git a/Sources/Datadog/Versioning.swift b/Sources/Datadog/Versioning.swift index afe3456a04..08f427428a 100644 --- a/Sources/Datadog/Versioning.swift +++ b/Sources/Datadog/Versioning.swift @@ -1,3 +1,3 @@ // GENERATED FILE: Do not edit directly -internal let sdkVersion = "1.7.0-beta4" +internal let sdkVersion = "1.7.0" From b12b2f4708d8f69241690b4034e52d4c729f2a44 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Wed, 22 Sep 2021 12:27:44 +0200 Subject: [PATCH 65/80] RUMM-1469 Fix: CI for fork PRs --- dependency-manager-tests/carthage/Cartfile.src | 2 +- dependency-manager-tests/carthage/Makefile | 9 ++++++++- dependency-manager-tests/cocoapods/Makefile | 8 +++++++- dependency-manager-tests/cocoapods/Podfile.src | 6 +++--- dependency-manager-tests/spm/Makefile | 8 +++++++- .../spm/SPMProject.xcodeproj.src/project.pbxproj | 4 ++-- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/dependency-manager-tests/carthage/Cartfile.src b/dependency-manager-tests/carthage/Cartfile.src index bb33b36630..0e05a40fe2 100644 --- a/dependency-manager-tests/carthage/Cartfile.src +++ b/dependency-manager-tests/carthage/Cartfile.src @@ -1 +1 @@ -github "DataDog/dd-sdk-ios" "REMOTE_GIT_REFERENCE" +github "GIT_REMOTE" "GIT_REFERENCE" diff --git a/dependency-manager-tests/carthage/Makefile b/dependency-manager-tests/carthage/Makefile index 30f515e429..f5542c32e9 100644 --- a/dependency-manager-tests/carthage/Makefile +++ b/dependency-manager-tests/carthage/Makefile @@ -6,9 +6,16 @@ ifneq (${BITRISE_GIT_TAG},) GIT_REFERENCE := ${BITRISE_GIT_TAG} endif +GIT_REMOTE := "DataDog/dd-sdk-ios" +ifneq (${BITRISEIO_PULL_REQUEST_REPOSITORY_URL},) + GIT_REMOTE := ${BITRISEIO_PULL_REQUEST_REPOSITORY_URL} +endif + test: @echo "⚙️ Configuring CTProject with remote branch: '${GIT_REFERENCE}'..." - @sed "s|REMOTE_GIT_REFERENCE|${GIT_REFERENCE}|g" Cartfile.src > Cartfile + + @sed "s|GIT_REFERENCE|${GIT_REFERENCE}|g" Cartfile.src | \ + sed "s|GIT_REMOTE|${GIT_REMOTE}|g" > Cartfile @rm -rf Carthage/ @echo "🧪 Run 'carthage update'" @carthage update --platform iOS --use-xcframeworks diff --git a/dependency-manager-tests/cocoapods/Makefile b/dependency-manager-tests/cocoapods/Makefile index 6a27fff1e2..30f0805f2f 100644 --- a/dependency-manager-tests/cocoapods/Makefile +++ b/dependency-manager-tests/cocoapods/Makefile @@ -6,9 +6,15 @@ ifneq (${BITRISE_GIT_TAG},) GIT_REFERENCE := "tag=\>\'${BITRISE_GIT_TAG}\'" endif +GIT_REMOTE := "https://github.com/DataDog/dd-sdk-ios.git" +ifneq (${BITRISEIO_PULL_REQUEST_REPOSITORY_URL},) + GIT_REMOTE := ${BITRISEIO_PULL_REQUEST_REPOSITORY_URL} +endif + test: @echo "⚙️ Configuring CPProject with remote branch: '${GIT_REFERENCE}'..." - @sed "s|REMOTE_GIT_REFERENCE|${GIT_REFERENCE}|g" Podfile.src > Podfile + @sed "s|GIT_REFERENCE|${GIT_REFERENCE}|g" Podfile.src | \ + sed "s|GIT_REMOTE|${GIT_REMOTE}|g" > Podfile @rm -rf Pods/ pod update @echo "👌 'pod update' OK" diff --git a/dependency-manager-tests/cocoapods/Podfile.src b/dependency-manager-tests/cocoapods/Podfile.src index dee2004ee1..17c7af061b 100644 --- a/dependency-manager-tests/cocoapods/Podfile.src +++ b/dependency-manager-tests/cocoapods/Podfile.src @@ -1,9 +1,9 @@ platform :ios, '13.0' abstract_target 'Common' do - pod 'DatadogSDK', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE - pod 'DatadogSDKAlamofireExtension', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE - pod 'DatadogSDKCrashReporting', :git => 'https://github.com/DataDog/dd-sdk-ios.git', :REMOTE_GIT_REFERENCE + pod 'DatadogSDK', :git => 'GIT_REMOTE', :GIT_REFERENCE + pod 'DatadogSDKAlamofireExtension', :git => 'GIT_REMOTE', :GIT_REFERENCE + pod 'DatadogSDKCrashReporting', :git => 'GIT_REMOTE', :GIT_REFERENCE pod 'Alamofire' target 'CPProjectUseFrameworks' do diff --git a/dependency-manager-tests/spm/Makefile b/dependency-manager-tests/spm/Makefile index 36c9f0928f..91f32f9a9c 100644 --- a/dependency-manager-tests/spm/Makefile +++ b/dependency-manager-tests/spm/Makefile @@ -6,11 +6,17 @@ ifneq (${BITRISE_GIT_TAG},) GIT_REFERENCE := ${BITRISE_GIT_TAG} endif +GIT_REMOTE := "https://github.com/DataDog/dd-sdk-ios" +ifneq (${BITRISEIO_PULL_REQUEST_REPOSITORY_URL},) + GIT_REMOTE := ${BITRISEIO_PULL_REQUEST_REPOSITORY_URL} +endif + test: @echo "⚙️ Configuring SPMProject with remote branch: '${GIT_REFERENCE}'..." @rm -rf SPMProject.xcodeproj @cp -r SPMProject.xcodeproj.src SPMProject.xcodeproj - @sed "s|REMOTE_GIT_REFERENCE|${GIT_REFERENCE}|g" SPMProject.xcodeproj.src/project.pbxproj > SPMProject.xcodeproj/project.pbxproj + @sed "s|GIT_REFERENCE|${GIT_REFERENCE}|g" SPMProject.xcodeproj.src/project.pbxproj | \ + sed "s|GIT_REMOTE|${GIT_REMOTE}|g" > SPMProject.xcodeproj/project.pbxproj @echo "OK 👌" create-src-from-xcodeproj: diff --git a/dependency-manager-tests/spm/SPMProject.xcodeproj.src/project.pbxproj b/dependency-manager-tests/spm/SPMProject.xcodeproj.src/project.pbxproj index 650b9ed423..810d7ecf50 100644 --- a/dependency-manager-tests/spm/SPMProject.xcodeproj.src/project.pbxproj +++ b/dependency-manager-tests/spm/SPMProject.xcodeproj.src/project.pbxproj @@ -678,9 +678,9 @@ /* Begin XCRemoteSwiftPackageReference section */ 9E73373E26B0123500917C24 /* XCRemoteSwiftPackageReference "dd-sdk-ios" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/DataDog/dd-sdk-ios/"; + repositoryURL = "GIT_REMOTE"; requirement = { - branch = REMOTE_GIT_REFERENCE; + branch = GIT_REFERENCE; kind = branch; }; }; From 1b5a997a94feb679bfb61892c8f777bdc2b2db7d Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Thu, 23 Sep 2021 12:41:26 +0200 Subject: [PATCH 66/80] RUMM-1625 Add more logs around environment status (simulator, runtimes and devices) when tool fails. --- .../generate_bitrise_yml.py | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/tools/nightly-unit-tests/generate_bitrise_yml.py b/tools/nightly-unit-tests/generate_bitrise_yml.py index b88bd2c5a2..a33cbebd20 100644 --- a/tools/nightly-unit-tests/generate_bitrise_yml.py +++ b/tools/nightly-unit-tests/generate_bitrise_yml.py @@ -23,6 +23,37 @@ from src.semver import Version +def get_environment() -> (Simulators, Runtimes, Devices): + """ + Uses `xcversion simulators` and `xcrun simctl` CLIs to load available + simulators, runtimes and devices. + """ + simulators = Simulators( + xcversion_simulators_output=shell_output('xcversion simulators') + ) + runtimes = Runtimes( + xcrun_simctl_list_runtimes_json_output=shell_output('xcrun simctl list runtimes --json') + ) + devices = Devices( + runtimes=runtimes, + xcrun_simctl_list_devices_json_output=shell_output('xcrun simctl list devices --json') + ) + return simulators, runtimes, devices + + +def dump_environment(simulators: Simulators, runtimes: Runtimes, devices: Devices): + """ + Prints log listing all available simulators, runtimes and devices. + """ + print('\n⚙️ All simulators:') + print_simulators(simulators=simulators.all) + print('\n⚙️ All runtimes:') + print_runtimes(runtimes=runtimes.all) + print('\n⚙️ All devices:') + print_devices(devices=devices.all) + + + def generate_bitrise_yml(test_plan: TestPlan, dry_run: bool): """ Generates `bitrise.yml` file for given Test Plan. @@ -55,22 +86,8 @@ def generate_bitrise_yml(test_plan: TestPlan, dry_run: bool): print(f' → installed {simulator.os_name} {simulator.os_version} Simulator, in: {minutes_elapsed}') # After installing new simulators, load list of available devices: - simulators = Simulators( - xcversion_simulators_output=shell_output('xcversion simulators') - ) - runtimes = Runtimes( - xcrun_simctl_list_runtimes_json_output=shell_output('xcrun simctl list runtimes --json') - ) - devices = Devices( - runtimes=runtimes, - xcrun_simctl_list_devices_json_output=shell_output('xcrun simctl list devices --json') - ) - print('\n⚙️ App simulators:') - print_simulators(simulators=simulators.all) - print('\n⚙️ All runtimes:') - print_runtimes(runtimes=runtimes.all) - print('\n⚙️ All devices:') - print_devices(devices=devices.all) + simulators, runtimes, devices = get_environment() + dump_environment(simulators=simulators, runtimes=runtimes, devices=devices) # Generate `bitrise.yml` print('\n⚙️ Creating `bitrise.yml`:') @@ -189,6 +206,9 @@ def create_test_plan(os_name: str, os_versions: [Version]) -> TestPlan: print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) + print('Environment dump:') + simulators, runtimes, devices = get_environment() + dump_environment(simulators=simulators, runtimes=runtimes, devices=devices) sys.exit(1) sys.exit(0) From a7b65fd4f66d965d31bb3c9f4f399822ef2b559d Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Thu, 23 Sep 2021 13:03:27 +0200 Subject: [PATCH 67/80] RUMM-1625 Do not plan test steps below minimal supported iOS version This is to prevent CI failures, when too old simulators can be installed on the host (CI machine). --- .../generate_bitrise_yml.py | 1 - tools/nightly-unit-tests/src/semver.py | 3 +++ tools/nightly-unit-tests/src/test_plan.py | 10 +++++++ tools/nightly-unit-tests/tests/test_semver.py | 26 +++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tools/nightly-unit-tests/tests/test_semver.py diff --git a/tools/nightly-unit-tests/generate_bitrise_yml.py b/tools/nightly-unit-tests/generate_bitrise_yml.py index a33cbebd20..a5615fa18c 100644 --- a/tools/nightly-unit-tests/generate_bitrise_yml.py +++ b/tools/nightly-unit-tests/generate_bitrise_yml.py @@ -53,7 +53,6 @@ def dump_environment(simulators: Simulators, runtimes: Runtimes, devices: Device print_devices(devices=devices.all) - def generate_bitrise_yml(test_plan: TestPlan, dry_run: bool): """ Generates `bitrise.yml` file for given Test Plan. diff --git a/tools/nightly-unit-tests/src/semver.py b/tools/nightly-unit-tests/src/semver.py index 71f803e0fa..e4a9e89b57 100644 --- a/tools/nightly-unit-tests/src/semver.py +++ b/tools/nightly-unit-tests/src/semver.py @@ -44,3 +44,6 @@ def is_newer_than(self, other_version: 'Version'): return True return False + + def is_newer_than_or_equal(self, other_version: 'Version'): + return self.is_newer_than(other_version=other_version) or self == other_version diff --git a/tools/nightly-unit-tests/src/test_plan.py b/tools/nightly-unit-tests/src/test_plan.py index 921ddfa3c1..7248201614 100644 --- a/tools/nightly-unit-tests/src/test_plan.py +++ b/tools/nightly-unit-tests/src/test_plan.py @@ -6,6 +6,7 @@ import random from src.simulators_parser import Simulator +from src.semver import Version # An estimated duration of installing simulator on Bitrise. # It includes ~3-5min margin over measured duration. @@ -19,6 +20,9 @@ # It includes ~10min margin for set-up and tear-down jobs. BITRISE_TIMEOUT_IN_MINUTES = 80 +# The minimal supported iOS version for running unit tests. +MIN_SUPPORTED_IOS_VERSION = Version.parse('11.0.0') + class TestPlanStep: def __init__(self, simulator: Simulator): @@ -62,6 +66,8 @@ def create_randomized_plan(simulators: [Simulator]): :return: a `TestPlan` object """ possible_steps = list(map(lambda s: TestPlanStep(simulator=s), simulators)) + possible_steps = list(filter(lambda step: is_using_supported_ios_version(step), possible_steps)) + planned_steps: [TestPlanStep] = [] random.shuffle(possible_steps) @@ -90,3 +96,7 @@ def total_duration_in_minutes(steps: [TestPlanStep]): for step in steps: total += step.estimated_duration_in_minutes return total + + +def is_using_supported_ios_version(step: TestPlanStep) -> bool: + return step.simulator.os_version.is_newer_than_or_equal(MIN_SUPPORTED_IOS_VERSION) diff --git a/tools/nightly-unit-tests/tests/test_semver.py b/tools/nightly-unit-tests/tests/test_semver.py new file mode 100644 index 0000000000..0689303308 --- /dev/null +++ b/tools/nightly-unit-tests/tests/test_semver.py @@ -0,0 +1,26 @@ +# ----------------------------------------------------------- +# 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. +# ----------------------------------------------------------- + +import unittest +from src.semver import Version + + +class VersionTestCase(unittest.TestCase): + def test_parsing(self): + self.assertEqual(Version.parse('10.0.3'), Version(major=10, minor=0, patch=3)) + self.assertEqual(Version.parse('11.4'), Version(major=11, minor=4, patch=0)) + self.assertEqual(Version.parse('12'), Version(major=12, minor=0, patch=0)) + + def test_comparing(self): + self.assertTrue(Version.parse('14.0.0').is_newer_than(Version.parse('13.1.2'))) + self.assertTrue(Version.parse('14.1.1').is_newer_than(Version.parse('14.1.0'))) + self.assertTrue(Version.parse('14.2.3').is_newer_than(Version.parse('14.2.2'))) + self.assertFalse(Version.parse('14.0.3').is_newer_than(Version.parse('15.0.2'))) + self.assertFalse(Version.parse('14.0.3').is_newer_than(Version.parse('14.1.0'))) + self.assertFalse(Version.parse('14.0.3').is_newer_than(Version.parse('14.0.4'))) + self.assertFalse(Version.parse('14.0.3').is_newer_than(Version.parse('14.0.3'))) + self.assertTrue(Version.parse('14.0.3').is_newer_than_or_equal(Version.parse('14.0.3'))) + self.assertFalse(Version.parse('14.0.2').is_newer_than_or_equal(Version.parse('14.0.3'))) From 4481075a5d5592de06910dbaf086b48ad7fa707d Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Thu, 23 Sep 2021 10:52:07 +0200 Subject: [PATCH 68/80] RUMM-1605 Use Native resource type instead of XHR --- Sources/Datadog/RUMMonitor.swift | 10 +++---- Sources/DatadogObjc/RUMMonitor+objc.swift | 2 ++ .../Datadog/Mocks/RUMDataModelMocks.swift | 2 +- .../SystemFrameworks/FoundationMocks.swift | 5 ++-- .../Scopes/RUMResourceScopeTests.swift | 2 +- .../Datadog/RUMMonitorTests.swift | 26 ++++++++++++------- .../DatadogObjc/DDRUMMonitorTests.swift | 1 + .../ObjcAPITests/DDRUMMonitor+apiTests.m | 1 + 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Sources/Datadog/RUMMonitor.swift b/Sources/Datadog/RUMMonitor.swift index 6fa5331837..a529557558 100644 --- a/Sources/Datadog/RUMMonitor.swift +++ b/Sources/Datadog/RUMMonitor.swift @@ -27,11 +27,11 @@ internal extension RUMResourceType { /// - Parameters: /// - request: the `URLRequest` for the resource. init?(request: URLRequest) { - let xhrHTTPMethods: Set = ["POST", "PUT", "DELETE"] + let nativeHTTPMethods: Set = ["POST", "PUT", "DELETE"] if let requestMethod = request.httpMethod?.uppercased(), - xhrHTTPMethods.contains(requestMethod) { - self = .xhr + nativeHTTPMethods.contains(requestMethod) { + self = .native } else { return nil } @@ -54,10 +54,10 @@ internal extension RUMResourceType { case ("font", _): self = .font case ("text", "css"): self = .css case ("text", "javascript"): self = .js - default: self = .xhr + default: self = .native } } else { - self = .xhr + self = .native } } } diff --git a/Sources/DatadogObjc/RUMMonitor+objc.swift b/Sources/DatadogObjc/RUMMonitor+objc.swift index 8324c0c31a..d6a700aa46 100644 --- a/Sources/DatadogObjc/RUMMonitor+objc.swift +++ b/Sources/DatadogObjc/RUMMonitor+objc.swift @@ -144,6 +144,7 @@ public enum DDRUMResourceType: Int { case js case media case other + case native internal var swiftType: RUMResourceType { switch self { @@ -156,6 +157,7 @@ public enum DDRUMResourceType: Int { case .font: return .font case .js: return .js case .media: return .media + case .native: return .native default: return .other } } diff --git a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift index 70fa40d024..f17277dd7d 100644 --- a/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/RUMDataModelMocks.swift @@ -143,7 +143,7 @@ extension RUMResourceEvent: RandomMockable { size: .mockRandom(), ssl: .init(duration: .mockRandom(), start: .mockRandom()), statusCode: .mockRandom(), - type: [.xhr, .fetch, .image].randomElement()!, + type: [.native, .image].randomElement()!, url: .mockRandom() ), service: .mockRandom(), diff --git a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift index e31592a06a..5e35d9aec2 100644 --- a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift @@ -379,13 +379,14 @@ extension URLResponse { static func mockWith( statusCode: Int = 200, - mimeType: String = "application/json" + mimeType: String? = "application/json" ) -> HTTPURLResponse { + let headers: [String: String] = (mimeType == nil) ? [:] : ["Content-Type": "\(mimeType!)"] return HTTPURLResponse( url: .mockAny(), statusCode: statusCode, httpVersion: nil, - headerFields: ["Content-Type": "\(mimeType)"] + headerFields: headers )! } } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift index 88ae95f9fd..118315e1ff 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMResourceScopeTests.swift @@ -356,7 +356,7 @@ class RUMResourceScopeTests: XCTestCase { } func testGivenResourceStartedWithKindBasedOnRequest_whenLoadingEndsWithDifferentKind_itSendsTheKindBasedOnRequest() throws { - let kinds: [RUMResourceType] = [.image, .xhr, .beacon, .css, .document, .fetch, .font, .js, .media, .other] + let kinds: [RUMResourceType] = [.image, .xhr, .beacon, .css, .document, .fetch, .font, .js, .media, .other, .native] let kindBasedOnRequest = kinds.randomElement()! let kindBasedOnResponse = kinds.randomElement()! diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index 0f10563533..690334a0de 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -117,7 +117,7 @@ class RUMMonitorTests: XCTestCase { } } - func testStartingView_thenLoadingXHRResourceWithRequestWithMetrics() throws { + func testStartingView_thenLoadingNativeResourceWithRequestWithMetrics() throws { guard #available(iOS 13, *) else { return // `URLSessionTaskMetrics` mocking doesn't work prior to iOS 13.0 } @@ -149,14 +149,14 @@ class RUMMonitorTests: XCTestCase { let session = try XCTUnwrap(try RUMSessionMatcher.groupMatchersBySessions(rumEventMatchers).first) let resourceEvent = session.viewVisits[0].resourceEvents[0] - XCTAssertEqual(resourceEvent.resource.type, .xhr, "POST Resources should always have the `.xhr` kind") + XCTAssertEqual(resourceEvent.resource.type, .native, "POST Resources should always have the `.native` kind") XCTAssertEqual(resourceEvent.resource.statusCode, 200) XCTAssertEqual(resourceEvent.resource.duration, 4_000_000_000) XCTAssertEqual(resourceEvent.resource.dns!.start, 1_000_000_000) XCTAssertEqual(resourceEvent.resource.dns!.duration, 2_000_000_000) } - func testStartingView_thenLoadingXHRResourceWithRequestWithExternalMetrics() throws { + func testStartingView_thenLoadingNativeResourceWithRequestWithExternalMetrics() throws { RUMFeature.instance = .mockByRecordingRUMEventMatchers(directories: temporaryFeatureDirectories) defer { RUMFeature.instance?.deinitialize() } @@ -200,7 +200,7 @@ class RUMMonitorTests: XCTestCase { let session = try XCTUnwrap(try RUMSessionMatcher.groupMatchersBySessions(rumEventMatchers).first) let resourceEvent = session.viewVisits[0].resourceEvents[0] - XCTAssertEqual(resourceEvent.resource.type, .xhr, "POST Resources should always have the `.xhr` kind") + XCTAssertEqual(resourceEvent.resource.type, .native, "POST Resources should always have the `.native` kind") XCTAssertEqual(resourceEvent.resource.statusCode, 200) XCTAssertEqual(resourceEvent.resource.duration, 12_000_000_000) @@ -1373,15 +1373,15 @@ class RUMResourceKindTests: XCTestCase { } } - func testWhenInitializedWithPOSTorPUTorDELETErequest_itReturnsXHR() { + func testWhenInitialized_itDefaultsToNative() { XCTAssertEqual( - RUMResourceType(request: .mockWith(httpMethod: "POST".randomcased())), .xhr + RUMResourceType(request: .mockWith(httpMethod: "POST".randomcased())), .native ) XCTAssertEqual( - RUMResourceType(request: .mockWith(httpMethod: "PUT".randomcased())), .xhr + RUMResourceType(request: .mockWith(httpMethod: "PUT".randomcased())), .native ) XCTAssertEqual( - RUMResourceType(request: .mockWith(httpMethod: "DELETE".randomcased())), .xhr + RUMResourceType(request: .mockWith(httpMethod: "DELETE".randomcased())), .native ) } @@ -1397,9 +1397,15 @@ class RUMResourceKindTests: XCTestCase { ) } - func testWhenInitializingFromHTTPURLResponse_itDefaultsToXhr() { + func testWhenInitializingFromHTTPURLResponseWithUnknownType_itDefaultsToNative() { XCTAssertEqual( - RUMResourceType(response: .mockWith(mimeType: "unknown/type")), .xhr + RUMResourceType(response: .mockWith(mimeType: "unknown/type")), .native + ) + } + + func testWhenInitializingFromHTTPURLResponseWithNoType_itDefaultsToNative() { + XCTAssertEqual( + RUMResourceType(response: .mockWith(mimeType: nil)), .native ) } } diff --git a/Tests/DatadogTests/DatadogObjc/DDRUMMonitorTests.swift b/Tests/DatadogTests/DatadogObjc/DDRUMMonitorTests.swift index 44c5f57327..c5ec326a60 100644 --- a/Tests/DatadogTests/DatadogObjc/DDRUMMonitorTests.swift +++ b/Tests/DatadogTests/DatadogObjc/DDRUMMonitorTests.swift @@ -97,6 +97,7 @@ class DDRUMResourceKindTests: XCTestCase { XCTAssertEqual(DDRUMResourceType.js.swiftType, .js) XCTAssertEqual(DDRUMResourceType.media.swiftType, .media) XCTAssertEqual(DDRUMResourceType.other.swiftType, .other) + XCTAssertEqual(DDRUMResourceType.native.swiftType, .native) } } diff --git a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m index 9c1fa93e75..d7bed85e1f 100644 --- a/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m +++ b/Tests/DatadogTests/DatadogObjc/ObjcAPITests/DDRUMMonitor+apiTests.m @@ -41,6 +41,7 @@ - (void)testDDRUMUserActionTypeAPI { - (void)testDDRUMResourceTypeAPI { DDRUMResourceTypeImage; DDRUMResourceTypeXhr; DDRUMResourceTypeBeacon; DDRUMResourceTypeCss; DDRUMResourceTypeDocument; DDRUMResourceTypeFetch; DDRUMResourceTypeFont; DDRUMResourceTypeJs; DDRUMResourceTypeMedia; DDRUMResourceTypeOther; + DDRUMResourceTypeNative; } - (void)testDDRUMMethodAPI { From 8f2a9c45196c5b9e9bb84378a37777616004b6c2 Mon Sep 17 00:00:00 2001 From: maxep Date: Fri, 24 Sep 2021 09:06:22 +0200 Subject: [PATCH 69/80] RUMM-1609 Use NTP server offset --- .../Core/System/Time/DateCorrector.swift | 19 ++++----- .../Core/System/Time/ServerDateProvider.swift | 42 ++++++++++++++----- .../System/Time/DateCorrectionTests.swift | 34 +++++++-------- .../SystemFrameworks/FoundationMocks.swift | 12 +++--- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/Sources/Datadog/Core/System/Time/DateCorrector.swift b/Sources/Datadog/Core/System/Time/DateCorrector.swift index 89068c04ac..a3f3dc9e4a 100644 --- a/Sources/Datadog/Core/System/Time/DateCorrector.swift +++ b/Sources/Datadog/Core/System/Time/DateCorrector.swift @@ -38,17 +38,17 @@ internal class DateCorrector: DateCorrectorType { self.serverDateProvider = serverDateProvider serverDateProvider.synchronize( with: DateCorrector.datadogNTPServers.randomElement()!, // swiftlint:disable:this force_unwrapping - completion: { serverTime in - let deviceTime = deviceDateProvider.currentDate() - if let serverTime = serverTime { - let difference = (serverTime.timeIntervalSince(deviceTime) * 1_000).rounded() / 1_000 + completion: { offset in + if let offset = offset { + let difference = (offset * 1_000).rounded() / 1_000 userLogger.info( """ NTP time synchronization completed. - Server time will be used for signing events (current server time is \(serverTime); \(difference)s difference with device time). + Server time will be used for signing events (\(difference)s difference with device time). """ ) } else { + let deviceTime = deviceDateProvider.currentDate() userLogger.warn( """ NTP time synchronization failed. @@ -61,13 +61,10 @@ internal class DateCorrector: DateCorrectorType { } var currentCorrection: DateCorrection { - if let serverTime = serverDateProvider.currentDate() { - let deviceTime = deviceDateProvider.currentDate() - return DateCorrection( - serverTimeOffset: serverTime.timeIntervalSince(deviceTime) - ) - } else { + guard let offset = serverDateProvider.offset else { return DateCorrection(serverTimeOffset: 0) } + + return DateCorrection(serverTimeOffset: offset) } } diff --git a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift index 64f0c21f33..b5fbb6c443 100644 --- a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift +++ b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift @@ -10,21 +10,41 @@ import Kronos /// Abstract the monotonic clock synchronized with the server using NTP. internal protocol ServerDateProvider { /// Start the clock synchronisation with NTP server. - /// Calls the `completion` by passing it the server time when the synchronization succeeds or`nil` if it fails. - func synchronize(with ntpPool: String, completion: @escaping (Date?) -> Void) - /// Returns the server time or `nil` if not yet determined. - /// This time gets more precise while synchronization is pending. - func currentDate() -> Date? + /// Calls the `completion` by passing it the server time offset when the synchronization succeeds or`nil` if it fails. + func synchronize(with pool: String, completion: @escaping (TimeInterval?) -> Void) + /// Returns the server time offset or `nil` if not yet determined. + /// This offset gets more precise while synchronization is pending. + var offset: TimeInterval? { get } } internal class NTPServerDateProvider: ServerDateProvider { - func synchronize(with ntpPool: String, completion: @escaping (Date?) -> Void) { - Clock.sync(from: ntpPool, completion: { serverTime, _ in - completion(serverTime) - }) + /// Server offset publisher. + private let publisher: ValuePublisher = ValuePublisher(initialValue: nil) + + /// Returns the server time offset or `nil` if not yet determined. + /// This offset gets more precise while synchronization is pending. + var offset: TimeInterval? { + return publisher.currentValue } - func currentDate() -> Date? { - return Clock.now + func synchronize(with pool: String, completion: @escaping (TimeInterval?) -> Void) { + // Kronos only notifies for the first and last samples. + // In case, the last sample does not return an offset, we calculate the offset + // from `Clock.now`. + Clock.sync( + from: pool, + first: { _, offset in + self.publisher.publishAsync(offset) + }, + completion: { now, offset in + if let offset = offset { + self.publisher.publishAsync(offset) + } else if let now = now { + self.publisher.publishAsync(now.timeIntervalSinceNow) + } + + completion(self.publisher.currentValue) + } + ) } } diff --git a/Tests/DatadogTests/Datadog/Core/System/Time/DateCorrectionTests.swift b/Tests/DatadogTests/Datadog/Core/System/Time/DateCorrectionTests.swift index bbf3f89a28..b40b3913fd 100644 --- a/Tests/DatadogTests/Datadog/Core/System/Time/DateCorrectionTests.swift +++ b/Tests/DatadogTests/Datadog/Core/System/Time/DateCorrectionTests.swift @@ -9,19 +9,15 @@ import XCTest private class ServerDateProviderMock: ServerDateProvider { private(set) var synchronizedNTPPool: String? = nil - var serverTime: Date? = nil + var offset: TimeInterval? = nil - init(using serverTime: Date? = nil) { - self.serverTime = serverTime + init(using offset: TimeInterval? = nil) { + self.offset = offset } - func synchronize(with ntpPool: String, completion: @escaping (Date?) -> Void) { - synchronizedNTPPool = ntpPool - completion(self.serverTime) - } - - func currentDate() -> Date? { - return serverTime + func synchronize(with pool: String, completion: @escaping (TimeInterval?) -> Void) { + synchronizedNTPPool = pool + completion(self.offset) } } @@ -57,8 +53,8 @@ class DateCorrectorTests: XCTestCase { } func testWhenNTPSynchronizationSucceeds_itPrintsInfoMessage() throws { - let serverDateProvider = ServerDateProviderMock(using: .mockDecember15th2019At10AMUTC()) - let deviceDateProvider = RelativeDateProvider(using: .mockDecember15th2019At10AMUTC(addingTimeInterval: 1)) + let serverDateProvider = ServerDateProviderMock(using: -1) + let deviceDateProvider = RelativeDateProvider(using: .mockRandomInThePast()) // When _ = DateCorrector(deviceDateProvider: deviceDateProvider, serverDateProvider: serverDateProvider) @@ -70,7 +66,7 @@ class DateCorrectorTests: XCTestCase { log.message, """ NTP time synchronization completed. - Server time will be used for signing events (current server time is 2019-12-15 10:00:00 +0000; -1.0s difference with device time). + Server time will be used for signing events (-1.0s difference with device time). """ ) } @@ -110,9 +106,7 @@ class DateCorrectorTests: XCTestCase { let serverDateProvider = ServerDateProviderMock(using: .mockRandomInThePast()) let deviceDateProvider = RelativeDateProvider(using: .mockRandomInThePast()) - func currentTimeDifference() -> TimeInterval { - serverDateProvider.currentDate()!.timeIntervalSince(deviceDateProvider.currentDate()) - } + var serverOffset: TimeInterval { serverDateProvider.offset! } // When let corrector = DateCorrector(deviceDateProvider: deviceDateProvider, serverDateProvider: serverDateProvider) @@ -121,7 +115,7 @@ class DateCorrectorTests: XCTestCase { XCTAssertTrue( datesEqual( corrector.currentCorrection.applying(to: deviceDateProvider.currentDate()), - serverDateProvider.currentDate()! + deviceDateProvider.currentDate().addingTimeInterval(serverOffset) ), "The device current time should be corrected to the server time." ) @@ -130,16 +124,16 @@ class DateCorrectorTests: XCTestCase { XCTAssertTrue( datesEqual( corrector.currentCorrection.applying(to: randomDeviceTime), - randomDeviceTime.addingTimeInterval(currentTimeDifference()) + randomDeviceTime.addingTimeInterval(serverOffset) ), "Any device time should be corrected by the server-to-device time difference." ) - serverDateProvider.serverTime = .mockRandomInThePast() + serverDateProvider.offset = .mockRandomInThePast() XCTAssertTrue( datesEqual( corrector.currentCorrection.applying(to: randomDeviceTime), - randomDeviceTime.addingTimeInterval(currentTimeDifference()) + randomDeviceTime.addingTimeInterval(serverOffset) ), "When the server time goes on, any next correction should include new server-to-device time difference." ) diff --git a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift index e31592a06a..e85fd05cce 100644 --- a/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift +++ b/Tests/DatadogTests/Datadog/Mocks/SystemFrameworks/FoundationMocks.swift @@ -119,7 +119,7 @@ extension Date: AnyMockable { } static func mockRandomInThePast() -> Date { - return Date(timeIntervalSinceReferenceDate: TimeInterval.random(in: 0.. Date { @@ -286,7 +286,7 @@ extension Float: AnyMockable { } extension Double: AnyMockable, RandomMockable { - static func mockAny() -> Float { + static func mockAny() -> Double { return 0 } @@ -300,11 +300,11 @@ extension Double: AnyMockable, RandomMockable { } extension TimeInterval { - static func mockAny() -> TimeInterval { - return 0 - } - static let distantFuture = TimeInterval(integerLiteral: .max) + + static func mockRandomInThePast() -> TimeInterval { + return random(in: 0.. Date: Fri, 24 Sep 2021 15:55:57 +0200 Subject: [PATCH 70/80] RUMM-1278 RefreshRate is scaled down to 0...60 --- Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift | 2 +- .../Datadog/RUM/RUMVitals/VitalInfoSamplerTests.swift | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift index e6120f8e50..76d59c6445 100644 --- a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift +++ b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift @@ -39,7 +39,7 @@ internal final class VitalInfoSampler { var refreshRate: VitalInfo { let info = refreshRatePublisher.currentValue - return info.scaledDown(by: maximumRefreshRate) + return info.scaledDown(by: maximumRefreshRate / 60.0) } private var timer: Timer? diff --git a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalInfoSamplerTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalInfoSamplerTests.swift index e3a5f997b1..6f05dedcc9 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalInfoSamplerTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalInfoSamplerTests.swift @@ -24,7 +24,7 @@ class VitalInfoSamplerTests: XCTestCase { mockMemoryReader.vitalData = 321.0 mockRefreshRateReader.vitalInfo = { var info = VitalInfo() - info.addSample(666.0) + info.addSample(60.0) return info }() @@ -38,8 +38,7 @@ class VitalInfoSamplerTests: XCTestCase { XCTAssertGreaterThan(sampler.cpu.sampleCount, 1) XCTAssertEqual(sampler.memory.meanValue, 321.0) XCTAssertGreaterThan(sampler.memory.sampleCount, 1) - let maxFPS = Double(UIScreen.main.maximumFramesPerSecond) - XCTAssertEqual(sampler.refreshRate.meanValue, 666.0 / maxFPS) + XCTAssertEqual(sampler.refreshRate.meanValue, 60.0) } } From 0d3b9b360fb40eb6bc9f2b62dbe8631c57f4a401 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Fri, 24 Sep 2021 16:11:02 +0200 Subject: [PATCH 71/80] RUMM-1278 PR comments addressed --- Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift index 76d59c6445..c8051eabe5 100644 --- a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift +++ b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift @@ -39,6 +39,8 @@ internal final class VitalInfoSampler { var refreshRate: VitalInfo { let info = refreshRatePublisher.currentValue + // NOTE: RUMM-1278 refreshRate is normalized to 0...60 range + // assuming 60 is the industry standard. return info.scaledDown(by: maximumRefreshRate / 60.0) } From 65d6a1bc4f23d34fdb45f839bc276905b868eb04 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Mon, 27 Sep 2021 10:02:22 +0200 Subject: [PATCH 72/80] RUMM-1278 PR comments addressed --- Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift index c8051eabe5..780f064a02 100644 --- a/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift +++ b/Sources/Datadog/RUM/RUMVitals/VitalInfoSampler.swift @@ -17,6 +17,12 @@ internal protocol ContinuousVitalReader { } internal final class VitalInfoSampler { + struct Constants { + // We use normalized 0...60 range for refresh rate in Mobile Vitals, + // assuming 60 is the industry standard. + static let normalizedRefreshRate = 60.0 + } + private static let frequency: TimeInterval = 1.0 let cpuReader: SamplingBasedVitalReader @@ -39,9 +45,7 @@ internal final class VitalInfoSampler { var refreshRate: VitalInfo { let info = refreshRatePublisher.currentValue - // NOTE: RUMM-1278 refreshRate is normalized to 0...60 range - // assuming 60 is the industry standard. - return info.scaledDown(by: maximumRefreshRate / 60.0) + return info.scaledDown(by: maximumRefreshRate / Constants.normalizedRefreshRate) } private var timer: Timer? From 30b0e877ccd7ec8d694baad82d843751a3569012 Mon Sep 17 00:00:00 2001 From: Xavier Gouchet Date: Mon, 27 Sep 2021 10:47:04 +0200 Subject: [PATCH 73/80] Update License to be recognised by Github --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index c6499bf710..479ecdd260 100644 --- a/LICENSE +++ b/LICENSE @@ -178,7 +178,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. From 1dbb19a6694d13e75bf3789845d2a6674006edc6 Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Mon, 27 Sep 2021 10:54:57 +0200 Subject: [PATCH 74/80] Update CHANGELOG.md --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77fdd5fd7b..5a47522382 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# 1.7.0 / 09-27-2021 + +### Changes + +* [BUGFIX] RUM: Fix `DDRUMView` API visibility for Objective-C. See [#583][] +* [FEATURE] Crash Reporting: Add `DatadogCrashReporting` +* [FEATURE] RUM: Add Mobile Vitals. See [#493][] [#514][] [#522][] [#495][] +* [FEATURE] RUM: Add option for renaming instrumented actions. See [#539][] +* [FEATURE] RUM: Add option for tracking events when app is in background. See [#504][] [#537][] +* [FEATURE] Add support for `us3` site. See [#523][] +* [IMPROVEMENT] RUM: Improve RUM <> APM integration. See [#524][] [#575][] [#531][] (Thanks [@jracollins][], [@marcusway][]) +* [IMPROVEMENT] RUM: Improve naming for views started with `key:`. See [#534][] +* [IMPROVEMENT] RUM: Improve actions instrumentation. See [#509][] [#545][] [#547][] +* [IMPROVEMENT] RUM: Sanitize custom timings for views. See [#525][] +* [IMPROVEMENT] Do not retry uploading events if Client Token is invalid. See [#535][] + # 1.6.0 / 06-09-2021 ### Changes @@ -200,6 +216,24 @@ [#479]: https://github.com/DataDog/dd-sdk-ios/issues/479 [#481]: https://github.com/DataDog/dd-sdk-ios/issues/481 [#483]: https://github.com/DataDog/dd-sdk-ios/issues/483 +[#493]: https://github.com/DataDog/dd-sdk-ios/issues/493 +[#495]: https://github.com/DataDog/dd-sdk-ios/issues/495 +[#504]: https://github.com/DataDog/dd-sdk-ios/issues/504 +[#509]: https://github.com/DataDog/dd-sdk-ios/issues/509 +[#514]: https://github.com/DataDog/dd-sdk-ios/issues/514 +[#522]: https://github.com/DataDog/dd-sdk-ios/issues/522 +[#523]: https://github.com/DataDog/dd-sdk-ios/issues/523 +[#524]: https://github.com/DataDog/dd-sdk-ios/issues/524 +[#525]: https://github.com/DataDog/dd-sdk-ios/issues/525 +[#531]: https://github.com/DataDog/dd-sdk-ios/issues/531 +[#534]: https://github.com/DataDog/dd-sdk-ios/issues/534 +[#535]: https://github.com/DataDog/dd-sdk-ios/issues/535 +[#537]: https://github.com/DataDog/dd-sdk-ios/issues/537 +[#539]: https://github.com/DataDog/dd-sdk-ios/issues/539 +[#545]: https://github.com/DataDog/dd-sdk-ios/issues/545 +[#547]: https://github.com/DataDog/dd-sdk-ios/issues/547 +[#575]: https://github.com/DataDog/dd-sdk-ios/issues/575 +[#583]: https://github.com/DataDog/dd-sdk-ios/issues/583 [@00FA9A]: https://github.com/00FA9A [@Britton-Earnin]: https://github.com/Britton-Earnin [@Hengyu]: https://github.com/Hengyu @@ -211,8 +245,10 @@ [@hyling]: https://github.com/hyling [@jegnux]: https://github.com/jegnux [@joeydong]: https://github.com/joeydong +[@jracollins]: https://github.com/jracollins [@lgaches]: https://github.com/lgaches [@lmramirez]: https://github.com/lmramirez +[@marcusway]: https://github.com/marcusway [@philtre]: https://github.com/philtre [@provTheodoreNewell]: https://github.com/provTheodoreNewell [@sdejesusF]: https://github.com/sdejesusF \ No newline at end of file From 8895375ff91cfd72f2086f4853546d9f51c9e1c9 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 27 Sep 2021 12:47:36 +0200 Subject: [PATCH 75/80] RUMM-1609 Keep weak self on Kronos callbacks --- .../Core/System/Time/ServerDateProvider.swift | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift index b5fbb6c443..1040a677d2 100644 --- a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift +++ b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift @@ -28,22 +28,23 @@ internal class NTPServerDateProvider: ServerDateProvider { } func synchronize(with pool: String, completion: @escaping (TimeInterval?) -> Void) { - // Kronos only notifies for the first and last samples. - // In case, the last sample does not return an offset, we calculate the offset - // from `Clock.now`. Clock.sync( from: pool, - first: { _, offset in - self.publisher.publishAsync(offset) + first: { [weak self] _, offset in + self?.publisher.publishAsync(offset) }, - completion: { now, offset in + completion: { [weak self] now, offset in + // Kronos only notifies for the first and last samples. + // In case, the last sample does not return an offset, we calculate the offset + // from the returned `now` parameter. The `now` parameter in this callback + // is `Clock.now`, so it is possible to have `now` but not `offset`. if let offset = offset { - self.publisher.publishAsync(offset) + self?.publisher.publishAsync(offset) } else if let now = now { - self.publisher.publishAsync(now.timeIntervalSinceNow) + self?.publisher.publishAsync(now.timeIntervalSinceNow) } - completion(self.publisher.currentValue) + completion(self?.publisher.currentValue) } ) } From 5eae9a8db0d47541f17385f636566f8725f853e8 Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 27 Sep 2021 14:53:45 +0200 Subject: [PATCH 76/80] RUMM-1609 Load Kronos clock for user defauls --- Sources/Datadog/Core/System/Time/ServerDateProvider.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift index 1040a677d2..97748d5ef1 100644 --- a/Sources/Datadog/Core/System/Time/ServerDateProvider.swift +++ b/Sources/Datadog/Core/System/Time/ServerDateProvider.swift @@ -47,5 +47,9 @@ internal class NTPServerDateProvider: ServerDateProvider { completion(self?.publisher.currentValue) } ) + + // `Kronos.sync` first loads the previous state from the `UserDefaults` if any. + // We can invoke `Clock.now` to retrieve the stored offset. + publisher.publishAsync(Clock.now?.timeIntervalSinceNow) } } From f1e018a1a44080ffd3f3a482490679ac3c793afa Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Fri, 13 Aug 2021 15:18:10 +0200 Subject: [PATCH 77/80] Merge pull request #563 from DataDog/buranmert/RUMM-1492-fix-isSystemImage-v2 RUMM-1492: Fix `isSystemImage` detection --- .../CrashReport.swift | 19 ++++++- .../CrashReportTests.swift | 52 ++++++++++++++----- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Sources/DatadogCrashReporting/PLCrashReporterIntegration/CrashReport.swift b/Sources/DatadogCrashReporting/PLCrashReporterIntegration/CrashReport.swift index f5d84d35c6..c002447ac0 100644 --- a/Sources/DatadogCrashReporting/PLCrashReporterIntegration/CrashReport.swift +++ b/Sources/DatadogCrashReporting/PLCrashReporterIntegration/CrashReport.swift @@ -241,8 +241,12 @@ extension BinaryImageInfo { self.uuid = imageUUID self.imageName = URL(string: imagePath)?.lastPathComponent - // NOTE: RUMM-1492 refer to JIRA ticket or `CrashReportTests.swift` to see imagePath examples - self.isSystemImage = !imagePath.contains("/Bundle/Application/") || imagePath.contains("/Contents/Developer/Platforms/") + + #if targetEnvironment(simulator) + self.isSystemImage = Self.isPathSystemImageInSimulator(imagePath) + #else + self.isSystemImage = Self.isPathSystemImageInDevice(imagePath) + #endif if let codeType = imageInfo.codeType { self.codeType = CodeType(from: codeType) @@ -254,6 +258,17 @@ extension BinaryImageInfo { self.imageBaseAddress = imageInfo.imageBaseAddress self.imageSize = imageInfo.imageSize } + + static func isPathSystemImageInSimulator(_ path: String) -> Bool { + // in simulator, example system image path: ~/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/... + return path.contains("/Contents/Developer/Platforms/") + } + + static func isPathSystemImageInDevice(_ path: String) -> Bool { + // in device, example user image path: .../containers/Bundle/Application/0000/Example.app/Frameworks/... + let isUserImage = path.contains("/Bundle/Application/") + return !isUserImage + } } extension BinaryImageInfo.CodeType { diff --git a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/CrashReportTests.swift b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/CrashReportTests.swift index d7e0adff0f..0866c78ca5 100644 --- a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/CrashReportTests.swift +++ b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/CrashReportTests.swift @@ -205,6 +205,37 @@ class CrashReportTests: XCTestCase { XCTAssertEqual(threadInfo.stackFrames.count, mockStackFrames.count) } + private let systemImagePaths_device = [ + "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore", + "/usr/lib/system/libdyld.dylib" + ] + private let systemImagePaths_simulator = [ + "/Users/john.appleseed/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" + ] + private let userImagePaths_device = [ + "/private/var/containers/Bundle/Application/0000/Example.app/Example", + "/private/var/containers/Bundle/Application/0000/Example.app/Frameworks/DatadogCrashReporting.framework/DatadogCrashReporting" + ] + private let userImagePaths_simulator = [ + "/Users/john.appleseed/Library/Developer/CoreSimulator/Devices/0000/data/Containers/Bundle/Application/0000/Example.app/Example", + "/Users/john.appleseed/Library/Developer/Xcode/DerivedData/Datadog-abcd/Build/Products/Release-iphonesimulator/DatadogCrashReporting.framework/DatadogCrashReporting" + ] + + func testItDetectsSystemImages() throws { + for systemImagePath in systemImagePaths_device { + XCTAssertTrue(BinaryImageInfo.isPathSystemImageInDevice(systemImagePath), "\(systemImagePath) is a system image") + } + for systemImagePath in systemImagePaths_simulator { + XCTAssertTrue(BinaryImageInfo.isPathSystemImageInSimulator(systemImagePath), "\(systemImagePath) is a system image") + } + for userImagePath in userImagePaths_device { + XCTAssertFalse(BinaryImageInfo.isPathSystemImageInDevice(userImagePath), "\(userImagePath) is an user image") + } + for userImagePath in userImagePaths_simulator { + XCTAssertFalse(BinaryImageInfo.isPathSystemImageInSimulator(userImagePath), "\(userImagePath) is an user image") + } + } + func testItReadsBinaryImageInfo() throws { func mock(with imagePath: URL) -> PLCrashReportMock.BinaryImageInfo { let mock = PLCrashReportMock.BinaryImageInfo() @@ -220,20 +251,13 @@ class CrashReportTests: XCTestCase { } // Given - let systemImagePathString = [ - "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore", - "/usr/lib/system/libdyld.dylib", - "/Users/john.appleseed/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" - ].randomElement()! - let systemImagePath = URL(string: systemImagePathString)! - - let userImagePathString = [ - "/private/var/containers/Bundle/Application/0000/Example.app/Example", - "/private/var/containers/Bundle/Application/0000/Example.app/Frameworks/DatadogCrashReporting.framework/DatadogCrashReporting", - "/Users/john.appleseed/Library/Developer/CoreSimulator/Devices/0000/data/Containers/Bundle/Application/0000/Example.app/Example", - "/Users/john.appleseed/Library/Developer/Xcode/DerivedData/Datadog-abcd/Build/Products/Release-iphonesimulator/DatadogCrashReporting.framework/DatadogCrashReporting" - ].randomElement()! - let userImagePath = URL(string: userImagePathString)! + #if targetEnvironment(simulator) + let systemImagePath = URL(string: systemImagePaths_simulator.randomElement()!)! + let userImagePath = URL(string: userImagePaths_simulator.randomElement()!)! + #else + let systemImagePath = URL(string: systemImagePaths_device.randomElement()!)! + let userImagePath = URL(string: userImagePaths_device.randomElement()!)! + #endif let mockSystemImage = mock(with: systemImagePath) let mockUserImage = mock(with: userImagePath) From 7024d9bcdb6e7e7ca27df5f81841a02ddd9f0bfe Mon Sep 17 00:00:00 2001 From: Maciek Grzybowski Date: Mon, 27 Sep 2021 15:25:09 +0200 Subject: [PATCH 78/80] Update `DDCrashReportBuilderTest` for iOS 15.0 --- .../PLCrashReporterIntegration/DDCrashReportBuilderTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportBuilderTests.swift b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportBuilderTests.swift index 8c185e19b9..e98adcffa8 100644 --- a/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportBuilderTests.swift +++ b/Tests/DatadogCrashReportingTests/PLCrashReporterIntegration/DDCrashReportBuilderTests.swift @@ -43,7 +43,8 @@ class DDCrashReportBuilderTests: XCTestCase { "`DDCrashReport` should include the image for `DatadogCrashReportingTests`" ) XCTAssertTrue( - ddCrashReport.binaryImages.contains(where: { $0.libraryName == "XCTest" }), + // Assert on prefix as it's `XCTestCore` on iOS 15+ and `XCTest` earlier: + ddCrashReport.binaryImages.contains(where: { $0.libraryName.hasPrefix("XCTest") }), "`DDCrashReport` should include the image for `XCTest`" ) } From 4fc3c05c3d06e9470c07ef22feae108786ce651e Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 27 Sep 2021 15:32:31 +0200 Subject: [PATCH 79/80] RUMM-1091 Harmonize code snippet in documentation --- docs/rum_collection/advanced_configuration.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/rum_collection/advanced_configuration.md b/docs/rum_collection/advanced_configuration.md index 1dd4669683..1779bcd8c8 100644 --- a/docs/rum_collection/advanced_configuration.md +++ b/docs/rum_collection/advanced_configuration.md @@ -65,10 +65,10 @@ Example: ```swift // in your `UIViewController`: -@IBAction func didTapDownloadResourceButton(_ sender: Any) { +@IBAction func didTapDownloadResourceButton(_ sender: UIButton) { Global.rum.addUserAction( type: .tap, - name: (sender as? UIButton).currentTitle ?? "", + name: sender.currentTitle ?? "", ) } ``` @@ -214,6 +214,7 @@ Inside the `rumView(for:)` implementation, your app should decide if a given `UI For instance, you can configure the predicate to use explicit type check for each view controller in your app: ```swift class YourCustomPredicate: UIKitRUMViewsPredicate { + func rumView(for viewController: UIViewController) -> RUMView? { switch viewController { case is HomeViewController: return .init(name: "Home") @@ -227,12 +228,13 @@ class YourCustomPredicate: UIKitRUMViewsPredicate { You can even come up with a more dynamic solution depending on your app's architecture. For example, if your view controllers use `accessibilityLabel` consistently, you can name views by the value of accessibility label: ```swift class YourCustomPredicate: UIKitRUMViewsPredicate { + func rumView(for viewController: UIViewController) -> RUMView? { - if let accessibilityLabel = viewController.accessibilityLabel { - return .init(name: accessibilityLabel) - } else { + guard let accessibilityLabel = viewController.accessibilityLabel else { return nil } + + return RUMView(name: accessibilityLabel) } } ``` From cb38e7806645cfbf675846de69d68c752612db3d Mon Sep 17 00:00:00 2001 From: maxep Date: Mon, 27 Sep 2021 18:01:19 +0200 Subject: [PATCH 80/80] RUMM-1091 Fix attributes keys --- docs/rum_collection/data_collected.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rum_collection/data_collected.md b/docs/rum_collection/data_collected.md index f6a847079f..c680bbeafb 100644 --- a/docs/rum_collection/data_collected.md +++ b/docs/rum_collection/data_collected.md @@ -79,7 +79,7 @@ You can enable [tracking user info][2] globally to collect and apply user attrib | Attribute name | Type | Description | |----------------|--------|-------------------------| -| `user.id` | string | Identifier of the user. | +| `usr.id` | string | Identifier of the user. | | `usr.name` | string | Name of the user. | | `usr.email` | string | Email of the user. | @@ -152,7 +152,7 @@ RUM action, error, resource and long task events contain information about the a | Metric | Type | Description | |--------------------------------|----------------|-------------------------------------------------------------------------------------------------| -| `duration` | number | Entire time spent loading the resource. | +| `resource.duration` | number | Entire time spent loading the resource. | | `resource.size` | number (bytes) | Resource size. | | `resource.connect.duration` | number (ns) | Time spent establishing a connection to the server (connectEnd - connectStart) | | `resource.ssl.duration` | number (ns) | Time spent for the TLS handshake. |