diff --git a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift index 93680472a8..224a37a569 100644 --- a/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift +++ b/Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift @@ -368,8 +368,8 @@ internal class RUMViewScope: RUMScope, RUMContextProvider { // RUMM-1779 Keep view active as long as we have ongoing resources let isActive = isActiveView || !resourceScopes.isEmpty - - let timeSpent = command.time.timeIntervalSince(viewStartTime) + // RUMM-2079 `time_spent` can't be lower than 1ns + let timeSpent = max(1e-9, command.time.timeIntervalSince(viewStartTime)) let cpuInfo = vitalInfoSampler.cpu let memoryInfo = vitalInfoSampler.memory let refreshRateInfo = vitalInfoSampler.refreshRate diff --git a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift index 8834c3f72e..da26c88094 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMMonitor/Scopes/RUMViewScopeTests.swift @@ -195,7 +195,7 @@ class RUMViewScopeTests: XCTestCase { XCTAssertEqual(event.view.name, "ViewName") let viewIsActive = try XCTUnwrap(event.view.isActive) XCTAssertTrue(viewIsActive) - XCTAssertEqual(event.view.timeSpent, 0) + XCTAssertEqual(event.view.timeSpent, 1) // Minimum `time_spent of 1 nanosecond XCTAssertEqual(event.view.action.count, isInitialView ? 1 : 0, "It must track application start action only if this is an initial view") XCTAssertEqual(event.view.error.count, 0) XCTAssertEqual(event.view.resource.count, 0) diff --git a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift index ac690fe5bf..7790c1a4df 100644 --- a/Tests/DatadogTests/Datadog/RUMMonitorTests.swift +++ b/Tests/DatadogTests/Datadog/RUMMonitorTests.swift @@ -887,6 +887,48 @@ class RUMMonitorTests: XCTestCase { } } + func testViewUpdateIdentifiedByViewController() throws { + let dateProvider = RelativeDateProvider(startingFrom: Date(), advancingBySeconds: 1) + let randomServiceName: String = .mockRandom() + RUMFeature.instance = .mockByRecordingRUMEventMatchers( + directories: temporaryFeatureDirectories, + configuration: .mockWith( + common: .mockWith(serviceName: randomServiceName) + ), + dependencies: .mockWith( + dateProvider: dateProvider + ) + ) + defer { RUMFeature.instance?.deinitialize() } + + let monitor = RUMMonitor.initialize() + setGlobalAttributes(of: monitor) + + monitor.startView(viewController: mockView) + monitor.stopView(viewController: mockView) + monitor.startView(viewController: mockView) + + let rumEventMatchers = try RUMFeature.waitAndReturnRUMEventMatchers(count: 4) + verifyGlobalAttributes(in: rumEventMatchers) + try rumEventMatchers[0].model(ofType: RUMActionEvent.self) { rumModel in + XCTAssertEqual(rumModel.action.type, .applicationStart) + XCTAssertEqual(rumModel.service, randomServiceName) + } + try rumEventMatchers[1].model(ofType: RUMViewEvent.self) { rumModel in + XCTAssertEqual(rumModel.view.action.count, 1) + XCTAssertEqual(rumModel.service, randomServiceName) + } + try rumEventMatchers[2].model(ofType: RUMViewEvent.self) { rumModel in + XCTAssertEqual(rumModel.view.action.count, 1) + XCTAssertEqual(rumModel.view.timeSpent, 1_000_000_000) + XCTAssertEqual(rumModel.service, randomServiceName) + } + try rumEventMatchers[3].model(ofType: RUMViewEvent.self) { rumModel in + XCTAssertEqual(rumModel.view.action.count, 0) + XCTAssertEqual(rumModel.service, randomServiceName) + } + } + // MARK: - Tracking Consent func testWhenChangingConsentValues_itUploadsOnlyAuthorizedRUMEvents() throws {