Skip to content

Commit

Permalink
Added/updated tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bsneed committed May 14, 2024
1 parent 0d9ab71 commit 2fa9d6d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Sources/Segment/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class Configuration {
var jsonNonConformingNumberStrategy: JSONSafeEncoder.NonConformingFloatEncodingStrategy = .zero
var storageMode: StorageMode = .disk
var anonymousIdGenerator: AnonymousIdGenerator = SegmentAnonymousId()
var httpSession: (() -> any HTTPSession)? = nil
var httpSession: (() -> any HTTPSession) = HTTPSessions.urlSession
}

internal var values: Values
Expand Down
9 changes: 1 addition & 8 deletions Sources/Segment/Utilities/Networking/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class HTTPClient {
self.apiHost = analytics.configuration.values.apiHost
self.cdnHost = analytics.configuration.values.cdnHost

self.session = Self.configuredSession(for: self.apiKey)
self.session = analytics.configuration.values.httpSession()
}

func segmentURL(for host: String, path: String) -> URL? {
Expand Down Expand Up @@ -199,11 +199,4 @@ extension HTTPClient {

return request
}

internal static func configuredSession(for writeKey: String) -> URLSession {
let configuration = URLSessionConfiguration.ephemeral
configuration.httpMaximumConnectionsPerHost = 2
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
return session
}
}
64 changes: 56 additions & 8 deletions Tests/Segment-Tests/HTTPClient_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,76 @@
// Created by Brandon Sneed on 1/21/21.
//

#if !os(Linux)

import XCTest
@testable import Segment

class HTTPClientTests: XCTestCase {

override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
RestrictedHTTPSession.reset()
}

override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

/*func testExample() throws {
func testCustomHTTPSessionUpload() throws {
// Use a specific writekey to this test so we do not collide with other cached items.
let analytics = Analytics(
configuration: Configuration(writeKey: "testCustomSesh")
.flushInterval(9999)
.flushAt(9999)
.httpSession(RestrictedHTTPSession())
)

waitUntilStarted(analytics: analytics)

analytics.storage.hardReset(doYouKnowHowToUseThis: true)

analytics.identify(userId: "brandon", traits: MyTraits(email: "[email protected]"))

let flushDone = XCTestExpectation(description: "flush done")
analytics.flush {
flushDone.fulfill()
}

wait(for: [flushDone])

XCTAssertEqual(RestrictedHTTPSession.fileUploads, 1)
}

func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.

func testDefaultHTTPSessionUpload() throws {
// Use a specific writekey to this test so we do not collide with other cached items.
let analytics = Analytics(
configuration: Configuration(writeKey: "testDefaultSesh")
.flushInterval(9999)
.flushAt(9999)
)

// reach in and set it, would be the same as the default ultimately
let segment = analytics.find(pluginType: SegmentDestination.self)
XCTAssertTrue(!(segment?.httpClient?.session is RestrictedHTTPSession))
XCTAssertTrue(segment?.httpClient?.session is URLSession)
segment?.httpClient?.session = RestrictedHTTPSession()

waitUntilStarted(analytics: analytics)

analytics.storage.hardReset(doYouKnowHowToUseThis: true)

analytics.identify(userId: "brandon", traits: MyTraits(email: "[email protected]"))

let flushDone = XCTestExpectation(description: "flush done")
analytics.flush {
flushDone.fulfill()
}
}*/


wait(for: [flushDone])

XCTAssertEqual(RestrictedHTTPSession.fileUploads, 1)
}
}

#endif
48 changes: 14 additions & 34 deletions Tests/Segment-Tests/StressTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class StressTests: XCTestCase {

override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
RestrictedHTTPSession.reset()
}

override func tearDownWithError() throws {
Expand All @@ -24,9 +25,13 @@ class StressTests: XCTestCase {
// register our network blocker
guard URLProtocol.registerClass(BlockNetworkCalls.self) else { XCTFail(); return }

let analytics = Analytics(configuration: Configuration(writeKey: "stressTest2").errorHandler({ error in
XCTFail("Storage Error: \(error)")
}))
let analytics = Analytics(configuration: Configuration(writeKey: "stressTest2")
.errorHandler({ error in
XCTFail("Storage Error: \(error)")
})
.httpSession(RestrictedHTTPSession())
)

analytics.purgeStorage()
analytics.storage.hardReset(doYouKnowHowToUseThis: true)

Expand All @@ -41,20 +46,6 @@ class StressTests: XCTestCase {

waitUntilStarted(analytics: analytics)

// set the httpclient to use our blocker session
let segment = analytics.find(pluginType: SegmentDestination.self)
let configuration = URLSessionConfiguration.ephemeral
configuration.allowsCellularAccess = true
configuration.timeoutIntervalForResource = 30
configuration.timeoutIntervalForRequest = 60
configuration.httpMaximumConnectionsPerHost = 2
configuration.protocolClasses = [BlockNetworkCalls.self]
configuration.httpAdditionalHeaders = ["Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic test",
"User-Agent": "analytics-ios/\(Analytics.version())"]
let blockSession = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
segment?.httpClient?.session = blockSession

@Atomic var ready = false
var queues = [DispatchQueue]()
for i in 0..<30 {
Expand Down Expand Up @@ -110,9 +101,12 @@ class StressTests: XCTestCase {
// register our network blocker
guard URLProtocol.registerClass(BlockNetworkCalls.self) else { XCTFail(); return }

let analytics = Analytics(configuration: Configuration(writeKey: "stressTest").errorHandler({ error in
XCTFail("Storage Error: \(error)")
}))
let analytics = Analytics(configuration: Configuration(writeKey: "stressTest2")
.errorHandler({ error in
XCTFail("Storage Error: \(error)")
})
.httpSession(RestrictedHTTPSession())
)
analytics.storage.hardReset(doYouKnowHowToUseThis: true)

DirectoryStore.fileValidator = { url in
Expand All @@ -126,20 +120,6 @@ class StressTests: XCTestCase {

waitUntilStarted(analytics: analytics)

// set the httpclient to use our blocker session
let segment = analytics.find(pluginType: SegmentDestination.self)
let configuration = URLSessionConfiguration.ephemeral
configuration.allowsCellularAccess = true
configuration.timeoutIntervalForResource = 30
configuration.timeoutIntervalForRequest = 60
configuration.httpMaximumConnectionsPerHost = 2
configuration.protocolClasses = [BlockNetworkCalls.self]
configuration.httpAdditionalHeaders = ["Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic test",
"User-Agent": "analytics-ios/\(Analytics.version())"]
let blockSession = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
segment?.httpClient?.session = blockSession

let writeQueue1 = DispatchQueue(label: "write queue 1", attributes: .concurrent)
let writeQueue2 = DispatchQueue(label: "write queue 2", attributes: .concurrent)
let writeQueue3 = DispatchQueue(label: "write queue 3", attributes: .concurrent)
Expand Down
55 changes: 55 additions & 0 deletions Tests/Segment-Tests/Support/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,61 @@ extension XCTestCase {

#if !os(Linux)

class RestrictedHTTPSession: HTTPSession {
let sesh: URLSession
static var fileUploads: Int = 0
static var dataUploads: Int = 0
static var dataTasks: Int = 0
static var invalidated: Int = 0

init(blocking: Bool = true, failing: Bool = false) {
let configuration = URLSessionConfiguration.ephemeral
configuration.allowsCellularAccess = true
configuration.timeoutIntervalForResource = 30
configuration.timeoutIntervalForRequest = 60
configuration.httpMaximumConnectionsPerHost = 2
configuration.httpAdditionalHeaders = ["Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic test",
"User-Agent": "analytics-ios/\(Analytics.version())"]

var protos = [URLProtocol.Type]()
if blocking { protos.append(BlockNetworkCalls.self) }
if failing { protos.append(FailedNetworkCalls.self) }
configuration.protocolClasses = protos

sesh = URLSession(configuration: configuration)
}

static func reset() {
fileUploads = 0
dataUploads = 0
dataTasks = 0
invalidated = 0
}

func uploadTask(with request: URLRequest, fromFile file: URL, completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void) -> URLSessionUploadTask {
defer { Self.fileUploads += 1 }
return sesh.uploadTask(with: request, fromFile: file, completionHandler: completionHandler)
}

func uploadTask(with request: URLRequest, from bodyData: Data?, completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void) -> URLSessionUploadTask {
defer { Self.dataUploads += 1 }
return sesh.uploadTask(with: request, from: bodyData, completionHandler: completionHandler)
}

func dataTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, (any Error)?) -> Void) -> URLSessionDataTask {
defer { Self.dataTasks += 1 }
return sesh.dataTask(with: request, completionHandler: completionHandler)
}

func finishTasksAndInvalidate() {
defer { Self.invalidated += 1 }
sesh.finishTasksAndInvalidate()
}
}



class BlockNetworkCalls: URLProtocol {
var initialURL: URL? = nil
override class func canInit(with request: URLRequest) -> Bool {
Expand Down

0 comments on commit 2fa9d6d

Please sign in to comment.