glean-core/ios/GleanTests/Net/BaselinePingTests.swift (99 lines of code) (raw):

/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @testable import Glean import OHHTTPStubs import OHHTTPStubsSwift import XCTest final class BaselinePingTests: XCTestCase { var expectation: XCTestExpectation? override func setUp() { resetGleanDiscardingInitialPings(testCase: self, tag: "GleanTests") } override func tearDown() { Glean.shared.testDestroyGleanHandle() expectation = nil tearDownStubs() } func testSendingOfForegroundBaselinePing() { stubServerReceive { _, json in // Check for the "dirty_startup" flag let pingInfo = json?["ping_info"] as? [String: Any] XCTAssertEqual("active", pingInfo?["reason"] as? String) // We may get error metrics in foreground pings, // so 'metrics' may exist. let metrics = json?["metrics"] as? [String: Any] if metrics != nil { // Since we are only expecting error metrics, // let's check that this is all we got. XCTAssertEqual(metrics?.count, 1, "metrics has more keys than expected: \(JSONStringify(metrics!))") let labeledCounters = metrics?["labeled_counter"] as? [String: Any] labeledCounters!.forEach { key, _ in XCTAssertTrue( key.starts(with: "glean.error") || key.starts(with: "glean.validation"), "Should only see glean.* counters, saw \(key)" ) } } DispatchQueue.main.async { // let the response get processed before we mark the expectation fulfilled self.expectation?.fulfill() } } // Set up the expectation that will be fulfilled by the stub above expectation = expectation(description: "Baseline Ping Received") // Set the last time the "metrics" ping was sent to now. This is required for us to not // send a metrics pings the first time we initialize Glean and to keep it from interfering // with these tests. let now = Date() Glean.shared.metricsPingScheduler!.updateSentDate(now) // Resetting Glean doesn't trigger lifecycle events in tests so we must call the method // invoked by the lifecycle observer directly. We must also reset the `isActive` flag in // order to have the correct state to test. Glean.shared.isActive = false Glean.shared.handleForegroundEvent() waitForExpectations(timeout: 5.0) { error in XCTAssertNil(error, "Test timed out waiting for upload: \(error!)") } } /* FIXME: This test causes crashes in subsequent tests, probably because of some race condition triggered by restarting Glean. func testSendingOfBaselinePingWithDirtyFlag() { // Set the dirty flag gleanSetDirtyFlag(true) // Set up the test stub based on the default telemetry endpoint stubServerReceive { pingType, json in XCTAssertEqual("baseline", pingType) XCTAssert(json != nil) // Check for the "dirty_startup" flag let pingInfo = json!["ping_info"] as! [String: Any] let reason = pingInfo["reason"] as! String if reason == "active" { // Skip initial "active" ping. // Glean is initialized ahead of this test and thus we might get one. return } XCTAssertEqual("dirty_startup", reason, "Expected a dirty_startup, got \(reason)") // 'metrics' will exist and include exactly one valid metric. // No errors should be reported. let metrics = json!["metrics"] as? [String: Any] if metrics != nil { if metrics!.count > 1 { XCTAssertEqual(metrics?.count, 1, "metrics has more keys than expected: \(JSONStringify(metrics!))") let labeledCounters = metrics?["labeled_counter"] as? [String: Any] labeledCounters!.forEach { key, _ in XCTAssertTrue( key.starts(with: "glean.error") || key.starts(with: "glean.validation"), "Should only see glean.* counters, saw \(key)" ) } } } DispatchQueue.main.async { // let the response get processed before we mark the expectation fulfilled self.expectation?.fulfill() } } // Set up the expectation that will be fulfilled by the stub above expectation = expectation(description: "Baseline Ping Received") // Set the last time the "metrics" ping was sent to now. This is required for us to not // send a metrics pings the first time we initialize Glean and to keep it from interfering // with these tests. let now = Date() Glean.shared.metricsPingScheduler.updateSentDate(now) // Restart Glean and don't clear the stores and then await the expectation Glean.shared.resetGlean(clearStores: false) waitForExpectations(timeout: 5.0) { error in XCTAssertNil(error, "Test timed out waiting for upload: \(error!)") } } */ func testSendingOfStartupBaselinePingWithAppLifetimeMetric() { // Set the dirty flag. gleanSetDirtyFlag(true) let stringMetric = StringMetricType(CommonMetricData( category: "telemetry", name: "app_lifetime", sendInPings: ["baseline"], lifetime: .application, disabled: false )) stringMetric.set("HELLOOOOO!") // Set up the test stub based on the default telemetry endpoint stubServerReceive { pingType, json in XCTAssertEqual("baseline", pingType) XCTAssert(json != nil) // Check for the "dirty_startup" flag let pingInfo = json!["ping_info"] as! [String: Any] let reason = pingInfo["reason"] as! String if reason == "active" { // Skip initial "active" ping. // Glean is initialized ahead of this test and thus we might get one. return } XCTAssertEqual("dirty_startup", reason) // Ensure there is only the expected locale string metric let metrics = json?["metrics"] as? [String: Any] let strings = metrics?["string"] as? [String: Any] let metric = strings?["telemetry.app_lifetime"] as? String XCTAssertEqual("HELLOOOOO!", metric) DispatchQueue.main.async { // let the response get processed before we mark the expectation fulfilled self.expectation?.fulfill() } } expectation = expectation(description: "baseline ping received") // Restart glean and don't clear the stores. // This should trigger a baseline ping with a "dirty_startup" reason. Glean.shared.resetGlean(clearStores: false) waitForExpectations(timeout: 5.0) { error in XCTAssertNil(error, "Test timed out waiting for upload: \(error!)") } } func testDisablingBaselinePing() { // Set up the test stub based on the default telemetry endpoint stubServerReceive { _, _ in XCTFail("Should not have recieved any ping") } // Set up the expectation that will NOT be fulfilled by the stub above. If it is // then it will trigger an assertion due to the `assertForOverFulfill` property. expectation = expectation(description: "Baseline Ping Received") // So we can wait for expectations below, we will go ahead and fulfill the // expectation. We want to assert if the ping is triggered and over fulfills it // from the stub above. expectation?.fulfill() // Set the last time the "metrics" ping was sent to now. This is required for us to not // send a metrics pings the first time we initialize Glean and to keep it from interfering // with these tests. let now = Date() Glean.shared.metricsPingScheduler!.updateSentDate(now) // Set a metric configuration that enables telemetry.counter_metric let metricConfigStringifiedJson = """ { "pings_enabled": { "baseline": false } } """ Glean.shared.applyServerKnobsConfig(metricConfigStringifiedJson) // Resetting Glean doesn't trigger lifecycle events in tests so we must call the method // invoked by the lifecycle observer directly. We must also reset the `isActive` flag in // order to have the correct state to test. Glean.shared.isActive = false Glean.shared.handleForegroundEvent() waitForExpectations(timeout: 5.0) { error in XCTAssertNil(error, "Test timed out waiting for upload: \(error!)") } } }