|
3 | 3 | @_spi(Private) import SentryTestUtils |
4 | 4 | import XCTest |
5 | 5 |
|
| 6 | +// swiftlint:disable file_length |
| 7 | +// This test class also includes tests for delayed frames calculation which is quite complex. |
| 8 | + |
6 | 9 | #if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) |
7 | 10 | class SentryFramesTrackerTests: XCTestCase { |
8 | 11 |
|
@@ -518,7 +521,167 @@ class SentryFramesTrackerTests: XCTestCase { |
518 | 521 | let actualFrameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
519 | 522 | XCTAssertEqual(actualFrameDelay.delayDuration, -1.0) |
520 | 523 | } |
521 | | - |
| 524 | + |
| 525 | + func testGetFramesDelay_WhenMovingFromBackgroundToForeground_BeforeDisplayLinkCalled() { |
| 526 | + // Arrange |
| 527 | + let sut = fixture.sut |
| 528 | + sut.start() |
| 529 | + |
| 530 | + let displayLink = fixture.displayLinkWrapper |
| 531 | + displayLink.call() |
| 532 | + _ = displayLink.slowestSlowFrame() |
| 533 | + |
| 534 | + let startSystemTime = fixture.dateProvider.systemTime() |
| 535 | + |
| 536 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
| 537 | + |
| 538 | + // Simulate app staying in background for 2 seconds |
| 539 | + fixture.dateProvider.advance(by: 2.0) |
| 540 | + |
| 541 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
| 542 | + let endSystemTime = fixture.dateProvider.systemTime() |
| 543 | + |
| 544 | + // Act |
| 545 | + let actualFrameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
| 546 | + |
| 547 | + // Assert |
| 548 | + |
| 549 | + // The frames tracer starts subscribing to the display link when an app moves to the foreground. Since |
| 550 | + // display link callbacks only occur when a new frame is drawn, it can take a couple of milliseconds |
| 551 | + // for the first display link callback to occur. We can only calculate frame statistics when having at |
| 552 | + // least one display link callback, as this marks the start of a new frame. |
| 553 | + XCTAssertEqual(actualFrameDelay.delayDuration, -1.0, accuracy: 0.0001) |
| 554 | + } |
| 555 | + |
| 556 | + func testGetFramesDelay_WhenMovingFromBackgroundToForeground_FirstFrameIsDrawing() { |
| 557 | + // Arrange |
| 558 | + let sut = fixture.sut |
| 559 | + sut.start() |
| 560 | + |
| 561 | + // Simulate some frames to establish system timestamps |
| 562 | + let displayLink = fixture.displayLinkWrapper |
| 563 | + displayLink.call() |
| 564 | + _ = displayLink.slowestSlowFrame() |
| 565 | + |
| 566 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
| 567 | + |
| 568 | + // Simulate app staying in background for 2 seconds |
| 569 | + fixture.dateProvider.advance(by: 2.0) |
| 570 | + |
| 571 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
| 572 | + |
| 573 | + displayLink.call() |
| 574 | + |
| 575 | + let startSystemTime = fixture.dateProvider.systemTime() |
| 576 | + fixture.dateProvider.advance(by: 0.01) |
| 577 | + let endSystemTime = fixture.dateProvider.systemTime() |
| 578 | + |
| 579 | + // Act |
| 580 | + let frameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
| 581 | + |
| 582 | + // The first is currently drawn, but it's not delayed yet. Therefore, 0 frame delay. |
| 583 | + XCTAssertEqual(frameDelay.delayDuration, 0.0, accuracy: 0.0001) |
| 584 | + } |
| 585 | + |
| 586 | + func testGetFramesDelay_WhenMovingFromBackgroundToForeground_FirstNormalFrameDrawn() { |
| 587 | + // Arrange |
| 588 | + let sut = fixture.sut |
| 589 | + sut.start() |
| 590 | + |
| 591 | + // Simulate some frames to establish system timestamps |
| 592 | + let displayLink = fixture.displayLinkWrapper |
| 593 | + displayLink.call() |
| 594 | + _ = displayLink.slowestSlowFrame() |
| 595 | + |
| 596 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
| 597 | + |
| 598 | + // Simulate app staying in background for 2 seconds |
| 599 | + fixture.dateProvider.advance(by: 2.0) |
| 600 | + |
| 601 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
| 602 | + |
| 603 | + displayLink.call() |
| 604 | + |
| 605 | + // The delayed frames tracker should also have its previous frame system timestamp reset |
| 606 | + // This prevents false delay calculations after unpausing |
| 607 | + let startSystemTime = fixture.dateProvider.systemTime() |
| 608 | + displayLink.normalFrame() |
| 609 | + let endSystemTime = fixture.dateProvider.systemTime() |
| 610 | + |
| 611 | + // Act |
| 612 | + let frameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
| 613 | + |
| 614 | + // Assert |
| 615 | + // Normal frame is drawn, no delay |
| 616 | + XCTAssertEqual(frameDelay.delayDuration, 0.0, accuracy: 0.0001) |
| 617 | + } |
| 618 | + |
| 619 | + func testGetFramesDelay_WhenMovingFromBackgroundToForeground_FirstFrameIsSlow() { |
| 620 | + // Arrange |
| 621 | + let sut = fixture.sut |
| 622 | + sut.start() |
| 623 | + |
| 624 | + // Simulate some frames to establish system timestamps |
| 625 | + let displayLink = fixture.displayLinkWrapper |
| 626 | + displayLink.call() |
| 627 | + _ = displayLink.slowestSlowFrame() |
| 628 | + |
| 629 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
| 630 | + |
| 631 | + // Simulate app staying in background for 2 seconds |
| 632 | + fixture.dateProvider.advance(by: 2.0) |
| 633 | + |
| 634 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
| 635 | + |
| 636 | + displayLink.call() |
| 637 | + |
| 638 | + // The delayed frames tracker should also have its previous frame system timestamp reset |
| 639 | + // This prevents false delay calculations after unpausing |
| 640 | + let startSystemTime = fixture.dateProvider.systemTime() |
| 641 | + _ = displayLink.slowestSlowFrame() |
| 642 | + let endSystemTime = fixture.dateProvider.systemTime() |
| 643 | + |
| 644 | + // Act |
| 645 | + let frameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
| 646 | + |
| 647 | + let expectedDelay = fixture.displayLinkWrapper.slowestSlowFrameDuration - slowFrameThreshold(fixture.displayLinkWrapper.currentFrameRate.rawValue) |
| 648 | + |
| 649 | + // Assert |
| 650 | + XCTAssertEqual(frameDelay.delayDuration, expectedDelay, accuracy: 0.0001) |
| 651 | + } |
| 652 | + |
| 653 | + func testGetFramesDelay_WhenMovingFromBackgroundToForeground_DelayBeforeBackgroundNotIncluded() { |
| 654 | + // Arrange |
| 655 | + let sut = fixture.sut |
| 656 | + sut.start() |
| 657 | + |
| 658 | + // Simulate some frames to establish system timestamps |
| 659 | + let displayLink = fixture.displayLinkWrapper |
| 660 | + displayLink.call() |
| 661 | + |
| 662 | + let startSystemTime = fixture.dateProvider.systemTime() |
| 663 | + |
| 664 | + _ = displayLink.slowestSlowFrame() |
| 665 | + |
| 666 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
| 667 | + |
| 668 | + // Simulate app staying in background for 2 seconds |
| 669 | + fixture.dateProvider.advance(by: 2.0) |
| 670 | + |
| 671 | + fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
| 672 | + |
| 673 | + displayLink.call() |
| 674 | + |
| 675 | + _ = displayLink.slowestSlowFrame() |
| 676 | + let endSystemTime = fixture.dateProvider.systemTime() |
| 677 | + |
| 678 | + // Act |
| 679 | + let frameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
| 680 | + |
| 681 | + // Assert |
| 682 | + XCTAssertEqual(frameDelay.delayDuration, -1.0, accuracy: 0.0001) |
| 683 | + } |
| 684 | + |
522 | 685 | func testFrameDelay_GetInfoFromBackgroundThreadWhileAdding() { |
523 | 686 | let sut = fixture.sut |
524 | 687 | sut.start() |
@@ -580,7 +743,7 @@ class SentryFramesTrackerTests: XCTestCase { |
580 | 743 |
|
581 | 744 | wait(for: [expectation], timeout: 3.0) |
582 | 745 | } |
583 | | - |
| 746 | + |
584 | 747 | func testAddMultipleListeners_AllCalledWithSameDate() { |
585 | 748 | let sut = fixture.sut |
586 | 749 | let listener1 = FrameTrackerListener() |
@@ -804,35 +967,6 @@ class SentryFramesTrackerTests: XCTestCase { |
804 | 967 | // Should not detect any slow or frozen frames from the pauses |
805 | 968 | try assert(slow: 0, frozen: 0, total: 4) |
806 | 969 | } |
807 | | - |
808 | | - func testUnpause_WithDelayedFramesTracker_ResetsPreviousFrameSystemTimestamp() { |
809 | | - let sut = fixture.sut |
810 | | - sut.start() |
811 | | - |
812 | | - // Simulate some frames to establish system timestamps |
813 | | - fixture.displayLinkWrapper.call() |
814 | | - fixture.displayLinkWrapper.normalFrame() |
815 | | - |
816 | | - // Pause the tracker |
817 | | - fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.willResignActiveNotification)) |
818 | | - |
819 | | - // Advance time significantly |
820 | | - fixture.dateProvider.advance(by: 5.0) |
821 | | - |
822 | | - // Unpause the tracker |
823 | | - fixture.notificationCenter.post(Notification(name: CrossPlatformApplication.didBecomeActiveNotification)) |
824 | | - |
825 | | - // The delayed frames tracker should also have its previous frame system timestamp reset |
826 | | - // This prevents false delay calculations after unpausing |
827 | | - let startSystemTime = fixture.dateProvider.systemTime() |
828 | | - fixture.dateProvider.advance(by: 0.001) |
829 | | - let endSystemTime = fixture.dateProvider.systemTime() |
830 | | - |
831 | | - let frameDelay = sut.getFramesDelay(startSystemTime, endSystemTimestamp: endSystemTime) |
832 | | - |
833 | | - // Should not report any delay from the pause period |
834 | | - XCTAssertEqual(frameDelay.delayDuration, 0.001, accuracy: 0.0001) |
835 | | - } |
836 | 970 |
|
837 | 971 | #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) |
838 | 972 | func testResetProfilingTimestamps_FromBackgroundThread() { |
@@ -941,3 +1075,5 @@ private extension SentryFramesTrackerTests { |
941 | 1075 | } |
942 | 1076 |
|
943 | 1077 | #endif |
| 1078 | + |
| 1079 | +// swiftlint:enable file_length |
0 commit comments