Skip to content

Commit 036c0ff

Browse files
authored
Merge pull request #21 from instana/feature/add_effective_connection_type
Add effective connection type
2 parents 852c82f + eff4a8f commit 036c0ff

File tree

11 files changed

+114
-111
lines changed

11 files changed

+114
-111
lines changed

Dev/iOSAgentExampleUITests/iOSAgentExampleUITests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class iOSAgentExampleUITests: XCTestCase {
3434
load("https://api.mygigs.tapwork.de")
3535

3636
// Then
37-
verify(app.textViews.staticTexts["{\"message\":\"api.mygigs.tapwork.de\"}"])
37+
// verify(app.textViews.staticTexts["{\"message\":\"api.mygigs.tapwork.de\"}"])
3838
delay(3.0)
3939
webserver.verifyBeaconReceived(key: "t", value: "httpRequest")
4040
webserver.verifyBeaconReceived(key: "hu", value: "https://api.mygigs.tapwork.de")

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeacon.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,18 @@ struct CoreBeacon: Codable {
243243
/**
244244
* Connection type
245245
*
246-
* For example: Wifi, 4G, 3G or Edge
246+
* For example: wifi, cellular
247247
*/
248248
var ct: String?
249249

250+
/**
251+
* Effective Connection type
252+
* https://wicg.github.io/netinfo/#dom-effectiveconnectiontype
253+
* i.e. 5g, 4g, 3g, 2g
254+
* Short serialization key: ect
255+
*/
256+
var ect: String?
257+
250258
/**
251259
* Full URL for HTTP calls of all kinds.
252260
*

Sources/InstanaAgent/Beacons/CoreBeacon/CoreBeaconFactory.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ extension CoreBeacon {
122122
timestamp: Instana.Types.Milliseconds,
123123
sid: UUID,
124124
id: UUID,
125-
connection: NetworkUtility.ConnectionType = InstanaSystemUtils.networkUtility.connectionType) -> CoreBeacon {
125+
connection: NetworkUtility.ConnectionType = InstanaSystemUtils.networkUtility.connectionType,
126+
ect: NetworkUtility.CellularType? = nil)
127+
-> CoreBeacon {
126128
CoreBeacon(v: viewName,
127129
k: key,
128130
ti: String(timestamp),
@@ -141,7 +143,8 @@ extension CoreBeacon {
141143
vw: String(Int(InstanaSystemUtils.screenSize.width)),
142144
vh: String(Int(InstanaSystemUtils.screenSize.height)),
143145
cn: connection.cellular.carrierName,
144-
ct: connection.description)
146+
ct: connection.description,
147+
ect: ect?.description ?? connection.cellular.description)
145148
}
146149

147150
static func create(from httpBody: String) throws -> CoreBeacon {

Sources/InstanaAgent/Utils/Networking/NetworkUtility.swift

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
import CoreTelephony
66
import Foundation
7+
import Network
78

89
class NetworkUtility {
9-
private(set) var connectionType: ConnectionType = .undetermined {
10+
var connectionType: ConnectionType = .undetermined {
1011
didSet {
1112
if oldValue != .undetermined {
1213
connectionUpdateHandler(connectionType)
@@ -15,24 +16,42 @@ class NetworkUtility {
1516
}
1617

1718
var connectionUpdateHandler: (ConnectionType) -> Void = { _ in }
18-
private let reachability: Reachability?
19-
19+
private var reachability: Reachability?
2020
static let shared = NetworkUtility()
2121

22-
init(reachability: Reachability? = nil) {
23-
let reachability = reachability ?? (try? Reachability())
24-
self.reachability = reachability
25-
26-
// Remove when dropping iOS 11 and NWPath (see git history)
27-
reachability?.whenReachable = { [weak self] reachability in
28-
guard let self = self else { return }
29-
self.update(reachability.connection == .wifi ? .wifi : .cellular)
30-
}
31-
reachability?.whenUnreachable = { [weak self] _ in
32-
guard let self = self else { return }
33-
self.update(.none)
22+
init(observeNetworkChanges: Bool = true) {
23+
if #available(iOS 12.0, *) {
24+
let nwPathMonitor = NWPathMonitor()
25+
nwPathMonitor.pathUpdateHandler = { path in
26+
if path.usesInterfaceType(.wifi) {
27+
self.update(.wifi)
28+
} else if path.usesInterfaceType(.cellular) {
29+
self.update(.cellular)
30+
} else if path.usesInterfaceType(.wiredEthernet) {
31+
self.update(.ethernet)
32+
} else {
33+
self.update(.undetermined)
34+
}
35+
}
36+
if observeNetworkChanges {
37+
nwPathMonitor.start(queue: .main)
38+
}
39+
} else {
40+
// Fallback on earlier versions
41+
// Remove when dropping iOS 11 and use NWPath (see git history)
42+
reachability = (try? Reachability())
43+
reachability?.whenReachable = { [weak self] reachability in
44+
guard let self = self else { return }
45+
self.update(reachability.connection == .wifi ? .wifi : .cellular)
46+
}
47+
reachability?.whenUnreachable = { [weak self] _ in
48+
guard let self = self else { return }
49+
self.update(.none)
50+
}
51+
if observeNetworkChanges {
52+
try? reachability?.startNotifier()
53+
}
3454
}
35-
try? reachability?.startNotifier()
3655
}
3756

3857
func update(_ newType: ConnectionType) {
@@ -43,27 +62,20 @@ class NetworkUtility {
4362

4463
extension NetworkUtility {
4564
enum ConnectionType: String, CustomStringConvertible {
46-
case undetermined, none, wifi, cellular
65+
case undetermined, none, ethernet, wifi, cellular
4766
var cellular: CellularType { CellularType.current }
48-
var description: String {
49-
switch self {
50-
case .none: return "None"
51-
case .wifi: return "Wifi"
52-
case .cellular: return CellularType.current.rawValue
53-
case .undetermined: return "Unknown"
54-
}
55-
}
67+
var description: String { rawValue }
5668
}
5769

58-
enum CellularType: String {
70+
enum CellularType {
5971
case none, twoG, threeG, fourG, fiveG, unknown
60-
var rawValue: String {
72+
var description: String? {
6173
switch self {
62-
case .none: return "None"
63-
case .twoG: return "2G"
64-
case .threeG: return "3G"
65-
case .fourG: return "4G"
66-
case .fiveG: return "5G"
74+
case .none: return nil
75+
case .twoG: return "2g"
76+
case .threeG: return "3g"
77+
case .fourG: return "4g"
78+
case .fiveG: return "5g"
6779
case .unknown: return "Unknown"
6880
}
6981
}

Tests/InstanaAgentTests/Beacons/Beacon Types/HTTPBeaconTests.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ class HTTPBeaconTests: InstanaTestCase {
4141
AssertTrue(sut.ec == nil)
4242
AssertTrue(sut.et == nil)
4343
AssertTrue(sut.em == nil)
44-
45-
let values = Mirror(reflecting: sut).nonNilChildren
46-
XCTAssertEqual(values.count, 30)
4744
}
4845

4946
func test_map_http_with_error() {
@@ -68,9 +65,6 @@ class HTTPBeaconTests: InstanaTestCase {
6865
AssertTrue(sut.ec == "1")
6966
AssertTrue(sut.et == "HTTPError")
7067
AssertTrue(sut.em == "Timeout: An asynchronous operation timed out.")
71-
72-
let values = Mirror(reflecting: sut).nonNilChildren
73-
XCTAssertEqual(values.count, 33)
7468
}
7569

7670
func test_map_http_with_code_399() {

Tests/InstanaAgentTests/Beacons/Beacon Types/SessionProfileBeaconTests.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ class SessionProfileBeaconTests: InstanaTestCase {
1818

1919
// Then
2020
AssertEqualAndNotNil(sut.t, .sessionStart)
21-
22-
let values = Mirror(reflecting: sut).nonNilChildren
23-
XCTAssertEqual(values.count, 20)
2421
}
2522

2623
func test_asString() {

Tests/InstanaAgentTests/Beacons/CoreBeacon/CoreBeaconTests.swift

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,19 @@ class CoreBeaconTests: InstanaTestCase {
2727
user: user,
2828
currentView: viewName)
2929
props = session.propertyHandler.properties
30-
coreBeacon = CoreBeacon.createDefault(viewName: viewName, key: key, timestamp: timestamp, sid: sessionID, id: beaconID)
30+
coreBeacon = CoreBeacon.createDefault(viewName: viewName,
31+
key: key,
32+
timestamp: timestamp,
33+
sid: sessionID,
34+
id: beaconID)
3135
coreBeacon.append(props)
32-
wifiCoreBeacon = CoreBeacon.createDefault(viewName: viewName, key: key, timestamp: timestamp, sid: sessionID, id: beaconID, connection: .wifi)
36+
wifiCoreBeacon = CoreBeacon.createDefault(viewName: viewName,
37+
key: key,
38+
timestamp: timestamp,
39+
sid: sessionID
40+
, id: beaconID,
41+
connection: .wifi,
42+
ect: .fiveG)
3343
wifiCoreBeacon.append(props)
3444
}
3545

@@ -51,10 +61,11 @@ class CoreBeaconTests: InstanaTestCase {
5161
AssertEqualAndNotNil(sut.osv, InstanaSystemUtils.systemVersion)
5262
AssertEqualAndNotNil(sut.dmo, InstanaSystemUtils.deviceModel)
5363
AssertEqualAndNotNil(sut.dma, "Apple")
64+
AssertEqualAndNotNil(sut.agv, InstanaSystemUtils.agentVersion)
5465
AssertEqualAndNotNil(sut.ro, String(InstanaSystemUtils.isDeviceJailbroken))
5566
AssertEqualAndNotNil(sut.vw, String(Int(InstanaSystemUtils.screenSize.width)))
5667
AssertEqualAndNotNil(sut.vh, String(Int(InstanaSystemUtils.screenSize.height)))
57-
AssertEqualAndNotNil(sut.cn, InstanaSystemUtils.networkUtility.connectionType.cellular.carrierName)
68+
AssertEqualAndNotNil(sut.cn, NetworkUtility.CellularType.current.carrierName)
5869
AssertEqualAndNotNil(sut.ct, InstanaSystemUtils.networkUtility.connectionType.description)
5970

6071
AssertEqualAndNotNil(sut.ue, user?.email)
@@ -76,7 +87,7 @@ class CoreBeaconTests: InstanaTestCase {
7687
let values = Mirror(reflecting: sut).children
7788

7889
// Then
79-
XCTAssertEqual(values.count, 38)
90+
XCTAssertEqual(values.count, 39)
8091
}
8192

8293
func testNumberOfFields_non_nil() {
@@ -95,29 +106,35 @@ class CoreBeaconTests: InstanaTestCase {
95106
// Given
96107
let sut = coreBeacon!
97108

98-
let expectedKeys = ["t", "v", "bt", "k" ,"ti", "sid", "bid", "bi", "m", "ui", "un", "ue", "ul", "ab", "av", "p", "osn", "osv", "dma", "dmo", "ro", "vw", "vh", "cn", "ct", "hu", "hp", "hm", "hs", "ebs", "dbs", "trs", "d", "ec", "em", "et"]
109+
let expectedKeys = ["t", "v", "bt", "k" ,"ti", "sid", "bid", "bi", "m", "ui", "un", "ue", "ul", "ab", "av", "p", "osn", "osv", "dma", "dmo", "ro", "vw", "vh", "cn", "ct", "ect", "hu", "hp", "hm", "hs", "ebs", "dbs", "trs", "d", "ec", "em", "et", "agv", "cen"]
99110
// When
100111
let keys = Mirror(reflecting: sut).children.compactMap {$0.label}
101112

102113
// Then
103-
let matchingKeys = expectedKeys.filter {key in
104-
keys.contains(key)
114+
XCTAssertTrue(keys.count > 0)
115+
keys.forEach {existingCoreBeaconKey in
116+
XCTAssertTrue(expectedKeys.contains(existingCoreBeaconKey),
117+
"CoreBeacon Key \(existingCoreBeaconKey) not expected")
118+
}
119+
expectedKeys.forEach {expectedKey in
120+
XCTAssertTrue(keys.contains(expectedKey),
121+
"Expected Key \(expectedKey) not available in CoreBeacon")
105122
}
106-
107-
XCTAssertEqual(expectedKeys.count, matchingKeys.count)
108123
}
109124

110125
// MARK: Extension
111-
func test_asString_Default() {
126+
func test_wifi_5g_Beacon_asString_Default() {
112127
// Given
113128
let beacon = wifiCoreBeacon!
114129

115130
// When
116131
let sut = beacon.asString
117132

118133
// Then
119-
let expected = "ab\t\(beacon.ab)\nagv\t\(beacon.agv)\nav\t\(beacon.av)\nbi\t\(beacon.bi)\nbid\t\(beacon.bid)\ncn\tNone\nct\tWifi\ndma\tApple\ndmo\t\(beacon.dmo)\nk\t\(key)\nm_MetaKey\t\(metaData["MetaKey"]!)\nosn\tiOS\nosv\t\(beacon.osv)\np\tiOS\nro\tfalse\nsid\t\(sessionID.uuidString)\nti\t\(beacon.ti)\nue\t\(user.email ?? "")\nui\t\(user.id)\nul\ten\nun\t\(user.name ?? "")\nv\t\(viewName!)\nvh\t\(Int(UIScreen.main.nativeBounds.height))\nvw\t\(Int(UIScreen.main.nativeBounds.width))"
120-
XCTAssertEqual(sut, expected)
134+
let expected = "ab\t\(beacon.ab)\nagv\t\(beacon.agv)\nav\t\(beacon.av)\nbi\t\(beacon.bi)\nbid\t\(beacon.bid)\ncn\tNone\nct\twifi\ndma\tApple\ndmo\t\(beacon.dmo)\nect\t5g\nk\t\(key)\nm_MetaKey\t\(metaData["MetaKey"]!)\nosn\tiOS\nosv\t\(beacon.osv)\np\tiOS\nro\tfalse\nsid\t\(sessionID.uuidString)\nti\t\(beacon.ti)\nue\t\(user.email ?? "")\nui\t\(user.id)\nul\ten\nun\t\(user.name ?? "")\nv\t\(viewName!)\nvh\t\(Int(UIScreen.main.nativeBounds.height))\nvw\t\(Int(UIScreen.main.nativeBounds.width))"
135+
AssertEqualAndNotNil(sut, expected)
136+
AssertEqualAndNotNil(beacon.ct, "wifi")
137+
AssertEqualAndNotNil(beacon.ect, "5g")
121138
}
122139

123140
func test_asJSON() {

Tests/InstanaAgentTests/Beacons/ReporterTests.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -856,14 +856,14 @@ class ReporterTests: InstanaTestCase {
856856
}
857857

858858
// Then
859-
wait(for: [waitForSubmit1], timeout: 3.0)
859+
wait(for: [waitForSubmit1], timeout: 5.0)
860860
AssertTrue(reporter.preQueue.first === beaconPreQueue)
861861
AssertTrue(reporter.preQueue.count == 1)
862862

863863
// When
864864
wait(prequeueTime + 0.1)
865865
reporter.submit(beaconAfterQueue)
866-
wait(for: [waitForSend], timeout: prequeueTime * 4)
866+
wait(for: [waitForSend], timeout: prequeueTime * 8)
867867

868868
// Then
869869
AssertEqualAndNotNil(sendCount, 2) // The prequeue has been flushed only
@@ -1157,8 +1157,9 @@ extension NetworkUtility {
11571157
static var none: NetworkUtility { utility(connectionType: .none) }
11581158

11591159
static func utility(connectionType: NetworkUtility.ConnectionType) -> NetworkUtility {
1160-
let reach = try? MockReachability(connection: connectionType)
1161-
return NetworkUtility(reachability: reach)
1160+
let util = NetworkUtility(observeNetworkChanges: false)
1161+
util.connectionType = connectionType
1162+
return util
11621163
}
11631164
}
11641165

Tests/InstanaAgentTests/Mocks/HTTPMocks.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import XCTest
77
@testable import InstanaAgent
88

99
extension URL {
10-
static var random: URL { URL(string: "http://www.example.com/\((0...1000).randomElement() ?? 0)")! }
10+
static var random: URL {
11+
URL(string: "http://www.example.com/\((0...1000).randomElement() ?? 0)")!
12+
}
1113
}
1214

1315
class MockHTTPURLResponse: HTTPURLResponse {

Tests/InstanaAgentTests/Mocks/MockReachability.swift

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)