Skip to content

Commit

Permalink
Merge pull request #419 from DataDog/ncreated/RUMM-1097-fix-default-r…
Browse files Browse the repository at this point in the history
…um-predicate-for-objc

RUMM-1097 Fix `DefaultUIKitRUMViewsPredicate` for Objc view controllers
  • Loading branch information
ncreated authored Feb 22, 2021
2 parents 196ec88 + a1118a0 commit c2b4e64
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 19 deletions.
10 changes: 10 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
6114FE23257671F00084E372 /* ConsentAwareDataWriterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE22257671F00084E372 /* ConsentAwareDataWriterTests.swift */; };
6114FE2F257687310084E372 /* ConsentProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE2E257687300084E372 /* ConsentProvider.swift */; };
6114FE3B25768AA90084E372 /* ConsentProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6114FE3A25768AA90084E372 /* ConsentProviderTests.swift */; };
6115299725E3BEF9004F740E /* UIKitExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6115299625E3BEF9004F740E /* UIKitExtensionsTests.swift */; };
61163C37252DDD60007DD5BF /* RUMMVSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C36252DDD60007DD5BF /* RUMMVSViewController.swift */; };
61163C3E252E0015007DD5BF /* RUMMVSModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C3D252E0015007DD5BF /* RUMMVSModalViewController.swift */; };
61163C4A252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61163C49252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift */; };
Expand Down Expand Up @@ -320,6 +321,7 @@
61D6FF7E24E53D3B00D0E375 /* BenchmarkMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D6FF7D24E53D3B00D0E375 /* BenchmarkMocks.swift */; };
61D980BA24E28D0100E03345 /* RUMIntegrations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D980B924E28D0100E03345 /* RUMIntegrations.swift */; };
61D980BC24E293F600E03345 /* RUMIntegrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61D980BB24E293F600E03345 /* RUMIntegrationsTests.swift */; };
61DB33B225DEDFC200F7EA71 /* CustomObjcViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61DB33B125DEDFC200F7EA71 /* CustomObjcViewController.m */; };
61DC6D922539E3E300FFAA22 /* LoggingCommonAsserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DC6D912539E3E300FFAA22 /* LoggingCommonAsserts.swift */; };
61E36A11254B2280001AD6F2 /* LaunchTimeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E36A10254B2280001AD6F2 /* LaunchTimeProvider.swift */; };
61E45BCF2450A6EC00F2C652 /* TracingUUIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E45BCE2450A6EC00F2C652 /* TracingUUIDTests.swift */; };
Expand Down Expand Up @@ -542,6 +544,7 @@
6114FE22257671F00084E372 /* ConsentAwareDataWriterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentAwareDataWriterTests.swift; sourceTree = "<group>"; };
6114FE2E257687300084E372 /* ConsentProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentProvider.swift; sourceTree = "<group>"; };
6114FE3A25768AA90084E372 /* ConsentProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentProviderTests.swift; sourceTree = "<group>"; };
6115299625E3BEF9004F740E /* UIKitExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtensionsTests.swift; sourceTree = "<group>"; };
61163C36252DDD60007DD5BF /* RUMMVSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMVSViewController.swift; sourceTree = "<group>"; };
61163C3D252E0015007DD5BF /* RUMMVSModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMMVSModalViewController.swift; sourceTree = "<group>"; };
61163C49252E03D6007DD5BF /* RUMModalViewsScenarioTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMModalViewsScenarioTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -772,6 +775,8 @@
61D6FF7D24E53D3B00D0E375 /* BenchmarkMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BenchmarkMocks.swift; sourceTree = "<group>"; };
61D980B924E28D0100E03345 /* RUMIntegrations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMIntegrations.swift; sourceTree = "<group>"; };
61D980BB24E293F600E03345 /* RUMIntegrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMIntegrationsTests.swift; sourceTree = "<group>"; };
61DB33B025DEDFC200F7EA71 /* CustomObjcViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomObjcViewController.h; sourceTree = "<group>"; };
61DB33B125DEDFC200F7EA71 /* CustomObjcViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomObjcViewController.m; sourceTree = "<group>"; };
61DC6D912539E3E300FFAA22 /* LoggingCommonAsserts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingCommonAsserts.swift; sourceTree = "<group>"; };
61E36A10254B2280001AD6F2 /* LaunchTimeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTimeProvider.swift; sourceTree = "<group>"; };
61E45BCE2450A6EC00F2C652 /* TracingUUIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingUUIDTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1307,6 +1312,7 @@
children = (
61133C362423990D00786299 /* InternalLoggersTests.swift */,
9E36D92124373EA700BFBDB7 /* SwiftExtensionsTests.swift */,
6115299625E3BEF9004F740E /* UIKitExtensionsTests.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -1362,6 +1368,8 @@
61F1A622249B811200075390 /* Encoding.swift */,
61A763DA252DB2B3005A23F2 /* NSURLSessionBridge.h */,
61A763DB252DB2B3005A23F2 /* NSURLSessionBridge.m */,
61DB33B025DEDFC200F7EA71 /* CustomObjcViewController.h */,
61DB33B125DEDFC200F7EA71 /* CustomObjcViewController.m */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -2951,6 +2959,7 @@
61133C5A2423990D00786299 /* FileTests.swift in Sources */,
61AD4E3A24534075006E34EA /* TracingFeatureTests.swift in Sources */,
61133C6B2423990D00786299 /* LogMatcher.swift in Sources */,
61DB33B225DEDFC200F7EA71 /* CustomObjcViewController.m in Sources */,
61F3CDAD25122C9200C816E5 /* UIKitRUMViewsHandlerTests.swift in Sources */,
61D980BC24E293F600E03345 /* RUMIntegrationsTests.swift in Sources */,
61363D9F24D99BAA0084CD6F /* DDErrorTests.swift in Sources */,
Expand Down Expand Up @@ -3055,6 +3064,7 @@
61133C4E2423990D00786299 /* UIKitMocks.swift in Sources */,
61B0387B252724AB00518F3C /* URLSessionTracingHandlerTests.swift in Sources */,
61133C4D2423990D00786299 /* CoreTelephonyMocks.swift in Sources */,
6115299725E3BEF9004F740E /* UIKitExtensionsTests.swift in Sources */,
614B0A4D24EBD71500A2A780 /* RUMUserInfoProviderTests.swift in Sources */,
9EF963E82537556300235F98 /* DDURLSessionDelegateAsSuperclassTests.swift in Sources */,
61133C552423990D00786299 /* BatteryStatusProviderTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@
</EnvironmentVariable>
<EnvironmentVariable
key = "DD_TEST_SCENARIO_CLASS_NAME"
value = "RUMResourcesScenario"
value = "RUMURLSessionResourcesScenario"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "DD_TEST_SCENARIO_CLASS_NAME"
value = "RUMNSURLSessionResourcesScenario"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
Expand Down
17 changes: 15 additions & 2 deletions Datadog/Example/Scenarios/RUM/RUMScenarios.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,25 @@ final class RUMTapActionScenario: TestScenario {

/// 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 RUMResourcesScenario: URLSessionBaseScenario, TestScenario {
final class RUMURLSessionResourcesScenario: URLSessionBaseScenario, TestScenario {
static let storyboardName = "URLSessionScenario"

override func configureSDK(builder: Datadog.Configuration.Builder) {
_ = builder
.trackUIKitRUMViews(using: DefaultUIKitRUMViewsPredicate())
.trackUIKitRUMViews()

super.configureSDK(builder: builder) // applies the `trackURLSession(firstPartyHosts:)`
}
}

/// Scenario which uses RUM and Tracing auto instrumentation features to track bunch of network requests
/// sent with `NSURLSession` from two VCs. The first VC calls first party resources, the second one calls third parties.
final class RUMNSURLSessionResourcesScenario: URLSessionBaseScenario, TestScenario {
static let storyboardName = "NSURLSessionScenario"

override func configureSDK(builder: Datadog.Configuration.Builder) {
_ = builder
.trackUIKitRUMViews()

super.configureSDK(builder: builder) // applies the `trackURLSession(firstPartyHosts:)`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Datadog
internal class TrackingConsentBaseScenario {
func configureSDK(builder: Datadog.Configuration.Builder) {
_ = builder
.trackUIKitRUMViews(using: DefaultUIKitRUMViewsPredicate())
.trackUIKitRUMViews()
.trackUIKitActions(true)
.trackURLSession(firstPartyHosts: ["datadoghq.com"])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
*/

#import "NSURLSessionBridge.h"
#import "CustomObjcViewController.h"
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,21 @@ public struct DefaultUIKitRUMViewsPredicate: UIKitRUMViewsPredicate {
public init () {}

public func rumView(for viewController: UIViewController) -> RUMView? {
let canonicalClassName = viewController.canonicalClassName
let isCustomClass = canonicalClassName.contains(".") // custom class contains module prefix

if isCustomClass {
var view = RUMView(name: canonicalClassName)
view.path = canonicalClassName
return view
} else {
guard !isUIKit(class: type(of: viewController)) else {
// Part of our heuristic for (auto) tracking view controllers is to ignore
// container view controllers coming from `UIKit` if they are not subclassed.
// This condition is wider and it ignores all view controllers defined in `UIKit` bundle.
return nil
}

let canonicalClassName = viewController.canonicalClassName
var view = RUMView(name: canonicalClassName)
view.path = canonicalClassName
return view
}

/// If given `class` comes from UIKit framework.
private func isUIKit(`class`: AnyClass) -> Bool {
return Bundle(for: `class`).isUIKit
}
}
4 changes: 4 additions & 0 deletions Sources/Datadog/Utils/UIKitExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ internal extension UIViewController {
return NSStringFromClass(type(of: self))
}
}

internal extension Bundle {
var isUIKit: Bool { bundleURL.lastPathComponent == "UIKitCore.framework" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,34 @@ private extension ExampleApplication {
}

class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts {
func testRUMResourcesScenario() throws {
private struct Expectations {
let expectedFirstPartyRequestsViewControllerName: String
let expectedThirdPartyRequestsViewControllerName: String
}

func testRUMURLSessionResourcesScenario() throws {
try runTest(
for: "RUMURLSessionResourcesScenario",
expectations: Expectations(
expectedFirstPartyRequestsViewControllerName: "Example.SendFirstPartyRequestsViewController",
expectedThirdPartyRequestsViewControllerName: "Example.SendThirdPartyRequestsViewController"
)
)
}

func testRUMNSURLSessionResourcesScenario() throws {
try runTest(
for: "RUMNSURLSessionResourcesScenario",
expectations: Expectations(
expectedFirstPartyRequestsViewControllerName: "ObjcSendFirstPartyRequestsViewController",
expectedThirdPartyRequestsViewControllerName: "ObjcSendThirdPartyRequestsViewController"
)
)
}

/// Both, `URLSession` (Swift) and `NSURLSession` (Objective-C) scenarios use different storyboards
/// and different view controllers to run this test, but the the logic and the instrumentation is the same.
private func runTest(for testScenarioClassName: String, expectations: Expectations) throws {
// Server session recording first party requests send to `HTTPServerMock`.
// Used to assert that trace propagation headers are send for first party requests.
let customFirstPartyServerSession = server.obtainUniqueRecordingSession()
Expand All @@ -40,7 +67,7 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts {

let app = ExampleApplication()
app.launchWith(
testScenarioClassName: "RUMResourcesScenario",
testScenarioClassName: testScenarioClassName,
serverConfiguration: HTTPServerMockConfiguration(
tracesEndpoint: tracingServerSession.recordingURL,
rumEndpoint: rumServerSession.recordingURL,
Expand Down Expand Up @@ -88,7 +115,8 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts {
XCTAssertEqual(session.viewVisits.count, 2)

// Asserts in `SendFirstPartyRequestsVC` RUM View
XCTAssertEqual(session.viewVisits[0].path, "Example.SendFirstPartyRequestsViewController")
XCTAssertEqual(session.viewVisits[0].name, expectations.expectedFirstPartyRequestsViewControllerName)
XCTAssertEqual(session.viewVisits[0].path, expectations.expectedFirstPartyRequestsViewControllerName)
XCTAssertEqual(session.viewVisits[0].resourceEvents.count, 2, "1st screen should track 2 RUM Resources")
XCTAssertEqual(session.viewVisits[0].errorEvents.count, 1, "1st screen should track 1 RUM Errors")

Expand Down Expand Up @@ -126,7 +154,8 @@ class RUMResourcesScenarioTests: IntegrationTests, RUMCommonAsserts {
XCTAssertEqual(firstPartyResourceError1.error.resource?.method, .get)

// Asserts in `SendThirdPartyRequestsVC` RUM View
XCTAssertEqual(session.viewVisits[1].path, "Example.SendThirdPartyRequestsViewController")
XCTAssertEqual(session.viewVisits[1].name, expectations.expectedThirdPartyRequestsViewControllerName)
XCTAssertEqual(session.viewVisits[1].path, expectations.expectedThirdPartyRequestsViewControllerName)
XCTAssertEqual(session.viewVisits[1].resourceEvents.count, 2, "2nd screen should track 2 RUM Resources")
XCTAssertEqual(session.viewVisits[1].errorEvents.count, 0, "2nd screen should track no RUM Errors")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,31 @@ import XCTest
import Datadog

class UIKitRUMViewsPredicateTests: XCTestCase {
func testGivenDefaultPredicate_whenAskingForCustomViewController_itNamesTheViewByItsClassName() {
func testGivenDefaultPredicate_whenAskingForCustomSwiftViewController_itNamesTheViewByItsClassName() {
// Given
let predicate = DefaultUIKitRUMViewsPredicate()

// When
let customViewController = createMockView(viewControllerClassName: "Module.CustomViewController")
let customViewController = createMockView(viewControllerClassName: "CustomSwiftViewController")
let rumView = predicate.rumView(for: customViewController)

// Then
XCTAssertEqual(rumView?.path, "Module.CustomViewController")
XCTAssertEqual(rumView?.name, "CustomSwiftViewController")
XCTAssertEqual(rumView?.path, "CustomSwiftViewController")
XCTAssertTrue(rumView!.attributes.isEmpty)
}

func testGivenDefaultPredicate_whenAskingForCustomObjcViewController_itNamesTheViewByItsClassName() {
// Given
let predicate = DefaultUIKitRUMViewsPredicate()

// When
let customViewController = CustomObjcViewController()
let rumView = predicate.rumView(for: customViewController)

// Then
XCTAssertEqual(rumView?.name, "CustomObjcViewController")
XCTAssertEqual(rumView?.path, "CustomObjcViewController")
XCTAssertTrue(rumView!.attributes.isEmpty)
}

Expand Down
39 changes: 39 additions & 0 deletions Tests/DatadogTests/Datadog/Utils/UIKitExtensionsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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
import UIKit
@testable import Datadog

class CustomSwiftViewController: UIViewController {}

class UIKitExtensionsTests: XCTestCase {
func testViewControllerCanonicalClassName() {
let swiftViewController = CustomSwiftViewController()
let objcViewController = CustomObjcViewController()

XCTAssertEqual(swiftViewController.canonicalClassName, "DatadogTests.CustomSwiftViewController")
XCTAssertEqual(objcViewController.canonicalClassName, "CustomObjcViewController")
}

func testBundleIsUIKit() {
let someUIKitClasses: [AnyClass] = [
UIViewController.self,
UIButton.self,
UINavigationBar.self,
UIScrollView.self
]

let someNonUIKitClasses: [AnyClass] = [
CustomSwiftViewController.self,
CustomObjcViewController.self,
OperationQueue.self,
]

someUIKitClasses.forEach { XCTAssertTrue(Bundle(for: $0).isUIKit) }
someNonUIKitClasses.forEach { XCTAssertFalse(Bundle(for: $0).isUIKit) }
}
}
15 changes: 15 additions & 0 deletions Tests/DatadogTests/Helpers/CustomObjcViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface CustomObjcViewController : UIViewController

@end

NS_ASSUME_NONNULL_END
19 changes: 19 additions & 0 deletions Tests/DatadogTests/Helpers/CustomObjcViewController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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 "CustomObjcViewController.h"

@interface CustomObjcViewController ()

@end

@implementation CustomObjcViewController

- (void)viewDidLoad {
[super viewDidLoad];
}

@end

0 comments on commit c2b4e64

Please sign in to comment.