-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
189 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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-Present Datadog, Inc. | ||
*/ | ||
|
||
import Foundation | ||
|
||
internal struct VitalInfo { | ||
/// Number of sample for this info | ||
public let sampleCount: Int | ||
|
||
/// Minimum value across all samples | ||
public let minValue: Double | ||
|
||
/// Maximum value across all samples | ||
public let maxValue: Double | ||
|
||
/// Average value across all samples | ||
public let meanValue: Double | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* 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-Present Datadog, Inc. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// A listener getting aggregated information from a VitalObserver | ||
internal protocol VitalListener { | ||
func onVitalInfo(info: VitalInfo) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* 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-Present Datadog, Inc. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// Provides interface for observing Vital info from a producer | ||
internal class VitalObserver: ValueObserver { | ||
|
||
let listener: VitalListener | ||
|
||
private var vitalInfo = VitalInfo( | ||
sampleCount: 0, | ||
minValue: Double.greatestFiniteMagnitude, | ||
maxValue: -Double.greatestFiniteMagnitude, | ||
meanValue: 0.0 | ||
) | ||
|
||
init(listener: VitalListener) { | ||
self.listener = listener | ||
} | ||
|
||
// MARK: - ValueObserver | ||
final func onValueChanged(oldValue: Double, newValue: Double) { | ||
let newSampleCount = vitalInfo.sampleCount + 1 | ||
// Assuming M(n) is the mean value of the first n samples | ||
// M(n) = ∑ sample(n) / n | ||
// n⨉M(n) = ∑ sample(n) | ||
// M(n+1) = ∑ sample(n+1) / (n+1) | ||
// = [ sample(n+1) + ∑ sample(n) ] / (n+1) | ||
// = (sample(n+1) + n⨉M(n)) / (n+1) | ||
let newMeanValue = (newValue + (Double(vitalInfo.sampleCount) * vitalInfo.meanValue)) / Double(newSampleCount) | ||
let newVitalInfo = VitalInfo( | ||
sampleCount: newSampleCount, | ||
minValue: min(vitalInfo.minValue, newValue), | ||
maxValue: max(vitalInfo.maxValue, newValue), | ||
meanValue: newMeanValue | ||
) | ||
vitalInfo = newVitalInfo | ||
listener.onVitalInfo(info: newVitalInfo) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalObserverTest.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* 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-Present Datadog, Inc. | ||
*/ | ||
|
||
import XCTest | ||
@testable import Datadog | ||
|
||
class VitalObserverTest: XCTestCase { | ||
func testItUpdatesVitalInfoOnFirstValue() { | ||
let randomOldValue = Double.random(in: -65_536.0...65_536.0) | ||
let randomValue = Double.random(in: -65_536.0...65_536.0) | ||
let notifyExpectation = expectation(description: "Notify vital info") | ||
let mockListener = VitalListenerMock() | ||
let testedObserver = VitalObserver(listener: mockListener) | ||
mockListener.onVitalInfoUpdate = { vitalInfo in | ||
XCTAssertEqual(vitalInfo.minValue, randomValue) | ||
XCTAssertEqual(vitalInfo.maxValue, randomValue) | ||
XCTAssertEqual(vitalInfo.meanValue, randomValue) | ||
XCTAssertEqual(vitalInfo.sampleCount, 1) | ||
notifyExpectation.fulfill() | ||
} | ||
|
||
// When | ||
testedObserver.onValueChanged(oldValue: randomOldValue, newValue: randomValue) | ||
|
||
// Then | ||
wait(for: [notifyExpectation], timeout: 0.5, enforceOrder: true) | ||
} | ||
|
||
func testItUpdatesVitalInfoOnMultipleValue() { | ||
let randomOldValue = Double.random(in: -65_536.0...65_536.0) | ||
let randomValue1 = Double.random(in: -65_536.0...65_536.0) | ||
let randomValue2 = Double.random(in: -65_536.0...65_536.0) | ||
let randomValue3 = Double.random(in: -65_536.0...65_536.0) | ||
let notifyExpectation1 = expectation(description: "Notify vital info 1") | ||
let notifyExpectation2 = expectation(description: "Notify vital info 2") | ||
let notifyExpectation3 = expectation(description: "Notify vital info 3") | ||
let mockListener = VitalListenerMock() | ||
let testedObserver = VitalObserver(listener: mockListener) | ||
|
||
// When | ||
mockListener.onVitalInfoUpdate = { vitalInfo in | ||
XCTAssertEqual(vitalInfo.minValue, randomValue1) | ||
XCTAssertEqual(vitalInfo.maxValue, randomValue1) | ||
XCTAssertEqual(vitalInfo.meanValue, randomValue1) | ||
XCTAssertEqual(vitalInfo.sampleCount, 1) | ||
notifyExpectation1.fulfill() | ||
} | ||
testedObserver.onValueChanged(oldValue: randomOldValue, newValue: randomValue1) | ||
mockListener.onVitalInfoUpdate = { vitalInfo in | ||
XCTAssertEqual(vitalInfo.minValue, min(randomValue1, randomValue2)) | ||
XCTAssertEqual(vitalInfo.maxValue, max(randomValue1, randomValue2)) | ||
XCTAssertEqual(vitalInfo.meanValue, (randomValue1 + randomValue2) / 2.0) | ||
XCTAssertEqual(vitalInfo.sampleCount, 2) | ||
notifyExpectation2.fulfill() | ||
} | ||
testedObserver.onValueChanged(oldValue: randomValue1, newValue: randomValue2) | ||
mockListener.onVitalInfoUpdate = { vitalInfo in | ||
XCTAssertEqual(vitalInfo.minValue, min(randomValue1, min(randomValue2, randomValue3))) | ||
XCTAssertEqual(vitalInfo.maxValue, max(randomValue1, max(randomValue2, randomValue3))) | ||
XCTAssertEqual(vitalInfo.meanValue, (randomValue1 + randomValue2 + randomValue3) / 3.0) | ||
XCTAssertEqual(vitalInfo.sampleCount, 3) | ||
notifyExpectation3.fulfill() | ||
} | ||
testedObserver.onValueChanged(oldValue: randomValue2, newValue: randomValue3) | ||
|
||
// Then | ||
wait(for: [notifyExpectation1, notifyExpectation2, notifyExpectation3], timeout: 0.5, enforceOrder: true) | ||
} | ||
} |