Compare commits

...

4 Commits

Author SHA1 Message Date
Shadowfacts 46db70d58b Fix building in release mode
When handleEvent dispatches to the other methods, it crashes the compiler
during an optimization pass. Seems to be related to:
https://github.com/apple/swift/issues/61350
2022-10-08 11:45:02 -04:00
Shadowfacts 21958eb77f Merge branch 'develop' into collection-timelines 2022-10-08 11:01:19 -04:00
Shadowfacts b30f149dc9 Use mutex on iOS 15 instead of os_unfair_lock
See #178
2022-10-08 10:57:59 -04:00
Shadowfacts 9b83566482 Fix TuskerTests not compiling 2022-10-08 10:55:55 -04:00
12 changed files with 249 additions and 183 deletions

View File

@ -37,6 +37,7 @@
D61A45E828DF477D002BE511 /* LoadingCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61A45E728DF477D002BE511 /* LoadingCollectionViewCell.swift */; };
D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61A45E928DF51EE002BE511 /* TimelineLikeCollectionViewController.swift */; };
D61ABEF628EE74D400B29151 /* StatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61ABEF528EE74D400B29151 /* StatusCollectionViewCell.swift */; };
D61ABEFC28F105DE00B29151 /* Pachyderm in Frameworks */ = {isa = PBXBuildFile; productRef = D61ABEFB28F105DE00B29151 /* Pachyderm */; };
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */; };
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */; };
D61AC1D9232EA42D00C54D2D /* InstanceTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */; };
@ -214,8 +215,6 @@
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11425B62E9700298D0F /* CacheExpiry.swift */; };
D6A6C11B25B63CEE00298D0F /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */; };
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
D6ADB6E828E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6E728E8C878009924AB /* PublicTimelineDescriptionCollectionViewCell.swift */; };
D6ADB6EA28E91C30009924AB /* TimelineStatusCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6E928E91C30009924AB /* TimelineStatusCollectionViewCell.swift */; };
D6ADB6EC28EA73CB009924AB /* StatusContentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ADB6EB28EA73CB009924AB /* StatusContentContainer.swift */; };
@ -695,8 +694,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */,
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */,
D61ABEFC28F105DE00B29151 /* Pachyderm in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1546,6 +1544,7 @@
);
name = TuskerUITests;
packageProductDependencies = (
D61ABEFB28F105DE00B29151 /* Pachyderm */,
);
productName = TuskerUITests;
productReference = D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */;
@ -2217,7 +2216,6 @@
MARKETING_VERSION = 2022.1;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = "";
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2246,7 +2244,6 @@
);
MARKETING_VERSION = 2022.1;
OTHER_CODE_SIGN_FLAGS = "";
"OTHER_SWIFT_FLAGS[sdk=iphone*14*]" = "$(inherited) -D SDK_IOS_14";
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -2262,14 +2259,15 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerTests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2283,14 +2281,15 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerTests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2303,14 +2302,14 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerUITests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2323,14 +2322,14 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerUITests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2483,6 +2482,10 @@
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
productName = SwiftSoup;
};
D61ABEFB28F105DE00B29151 /* Pachyderm */ = {
isa = XCSwiftPackageProductDependency;
productName = Pachyderm;
};
D6552366289870790048A653 /* ScreenCorners */ = {
isa = XCSwiftPackageProductDependency;
package = D6552365289870790048A653 /* XCRemoteSwiftPackageReference "ScreenCorners" */;

View File

@ -19,7 +19,7 @@ class MultiThreadDictionary<Key: Hashable & Sendable, Value: Sendable> {
if #available(iOS 16.0, *) {
self.lock = OSAllocatedUnfairLock(initialState: [:])
} else {
self.lock = UnfairLock(initialState: [:])
self.lock = MutexLock(initialState: [:])
}
}
@ -65,21 +65,41 @@ fileprivate protocol Lock<State> {
extension OSAllocatedUnfairLock: Lock {
}
// from http://www.russbishop.net/the-law
fileprivate class UnfairLock<State>: Lock {
private var lock: UnsafeMutablePointer<os_unfair_lock>
// something is wrong with the UnfairLock impl and it results in segv_accerrs
fileprivate class MutexLock<State>: Lock {
private var state: State
private var lock = NSLock()
init(initialState: State) {
self.state = initialState
self.lock = .allocate(capacity: 1)
self.lock.initialize(to: os_unfair_lock())
}
deinit {
self.lock.deallocate()
}
func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable {
os_unfair_lock_lock(lock)
defer { os_unfair_lock_unlock(lock) }
func withLock<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable {
if !lock.lock(before: Date(timeIntervalSinceNow: 1)) {
// if we can't acquire the lock after 1 second, something has gone catastrophically wrong
fatalError()
}
defer { lock.unlock() }
return try body(&state)
}
}
//// from http://www.russbishop.net/the-law
//fileprivate class UnfairLock<State>: Lock {
// private var lock: UnsafeMutablePointer<os_unfair_lock>
// private var state: State
// init(initialState: State) {
// self.state = initialState
// self.lock = .allocate(capacity: 1)
// self.lock.initialize(to: os_unfair_lock())
// }
// deinit {
// self.lock.deinitialize(count: 1)
// self.lock.deallocate()
// }
// func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable {
// os_unfair_lock_lock(lock)
// defer { os_unfair_lock_unlock(lock) }
// return try body(&state)
// }
//}

View File

@ -63,27 +63,6 @@ extension TimelineLikeCollectionViewController {
}
}
func handleEvent(_ event: TimelineLikeController<TimelineItem>.Event) async {
switch event {
case .addLoadingIndicator:
await handleAddLoadingIndicator()
case .removeLoadingIndicator:
await handleRemoveLoadingIndicator()
case .loadAllError(let error, _):
await handleLoadAllError(error)
case .replaceAllItems(let items, _):
await handleReplaceAllItems(items)
case .loadNewerError(let error, _):
await handleLoadNewerError(error)
case .prependItems(let items, _):
await handlePrependItems(items)
case .loadOlderError(let error, _):
await handleLoadOlderError(error)
case .appendItems(let items, _):
await handleAppendItems(items)
}
}
func handleAddLoadingIndicator() async {
var snapshot = dataSource.snapshot()
if !snapshot.sectionIdentifiers.contains(.footer) {

View File

@ -20,7 +20,14 @@ protocol TimelineLikeControllerDelegate<TimelineItem>: AnyObject {
func canLoadOlder() async -> Bool
func handleEvent(_ event: TimelineLikeController<TimelineItem>.Event) async
func handleAddLoadingIndicator() async
func handleRemoveLoadingIndicator() async
func handleLoadAllError(_ error: Swift.Error) async
func handleReplaceAllItems(_ timelineItems: [TimelineItem]) async
func handleLoadNewerError(_ error: Swift.Error) async
func handlePrependItems(_ timelineItems: [TimelineItem]) async
func handleLoadOlderError(_ error: Swift.Error) async
func handleAppendItems(_ timelineItems: [TimelineItem]) async
}
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "TimelineLikeController")
@ -112,7 +119,24 @@ actor TimelineLikeController<Item> {
private func emit(event: Event) async {
precondition(state.canEmit(event: event))
await delegate.handleEvent(event)
switch event {
case .addLoadingIndicator:
await delegate.handleAddLoadingIndicator()
case .removeLoadingIndicator:
await delegate.handleRemoveLoadingIndicator()
case .loadAllError(let error, _):
await delegate.handleLoadAllError(error)
case .replaceAllItems(let items, _):
await delegate.handleReplaceAllItems(items)
case .loadNewerError(let error, _):
await delegate.handleLoadNewerError(error)
case .prependItems(let items, _):
await delegate.handlePrependItems(items)
case .loadOlderError(let error, _):
await delegate.handleLoadOlderError(error)
case .appendItems(let items, _):
await delegate.handleAppendItems(items)
}
}
enum State: Equatable, CustomDebugStringConvertible {

View File

@ -30,5 +30,45 @@ class TuskerTests: XCTestCase {
// Put the code you want to measure the time of here.
}
}
func testFuckingLock() {
let lock = MutexLock<[Int: Bool]>(initialState: [:])
for i in 0..<100 {
Thread.detachNewThread {
for j in 0..<50_000 {
lock.withLock {
$0[i * 50_000 + j] = true
}
}
}
}
while true {
if lock.withLock({ $0.count }) == 5_000_000 {
break
}
}
lock.withLock({ _ in
print("WHAT THE FUUUUUUUUUUUUCK")
})
}
}
fileprivate class MutexLock<State> {
private var state: State
private var lock = NSLock()
init(initialState: State) {
self.state = initialState
}
func withLock<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable {
if !lock.lock(before: Date(timeIntervalSinceNow: 1)) {
// if we can't acquire the lock after 1 second, something has gone catastrophically wrong
fatalError()
}
defer { lock.unlock() }
return try body(&state)
}
}

View File

@ -7,70 +7,70 @@
//
import Foundation
import Ambassador
fileprivate let notFound = ["error": "Record not found"]
extension Router {
func allRoutes() {
instanceRoutes()
accountRoutes()
timelineRoutes()
notificationRoutes()
}
func instanceRoutes() {
self["/api/v1/instance"] = JSONResponse(handler: { (_) in
return [
"description": "An instance description",
"max_toot_chars": 500,
"thumbnail": "http://localhost:8080/thumbnail.png",
"title": "Localhost",
"uri": "http://localhost:8080",
"version": "2.7.2",
"urls": [:]
]
})
}
func accountRoutes() {
let selfAccount: [String: Any] = [
"id": "1",
"username": "admin",
"acct": "admin",
"display_name": "Admin Account",
"locked": false,
"created_at": "2019-12-31T11:13:42.0Z",
"followers_count": 0,
"following_count": 0,
"statuses_count": 0,
"note": "My profile description.",
"url": "http://localhost:8080/users/admin",
"avatar": "http://localhost:8080/avatar/admin.jpg",
"avatar_static": "http://localhost:8080/avatar/admin.jpg",
"header": "http://localhost:8080/header/admin.jpg",
"header_static": "http://localhost:8080/header/admin.jpg",
"emojis": []
]
self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (ctx) in
if ctx.captures[0] == "1" {
return JSONResponse(result: selfAccount)
} else {
return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
}
}
}
func timelineRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
}
func notificationRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
}
}
//import Ambassador
//
//fileprivate let notFound = ["error": "Record not found"]
//
//extension Router {
// func allRoutes() {
// instanceRoutes()
// accountRoutes()
// timelineRoutes()
// notificationRoutes()
// }
//
// func instanceRoutes() {
// self["/api/v1/instance"] = JSONResponse(handler: { (_) in
// return [
// "description": "An instance description",
// "max_toot_chars": 500,
// "thumbnail": "http://localhost:8080/thumbnail.png",
// "title": "Localhost",
// "uri": "http://localhost:8080",
// "version": "2.7.2",
// "urls": [:]
// ]
// })
// }
//
// func accountRoutes() {
// let selfAccount: [String: Any] = [
// "id": "1",
// "username": "admin",
// "acct": "admin",
// "display_name": "Admin Account",
// "locked": false,
// "created_at": "2019-12-31T11:13:42.0Z",
// "followers_count": 0,
// "following_count": 0,
// "statuses_count": 0,
// "note": "My profile description.",
// "url": "http://localhost:8080/users/admin",
// "avatar": "http://localhost:8080/avatar/admin.jpg",
// "avatar_static": "http://localhost:8080/avatar/admin.jpg",
// "header": "http://localhost:8080/header/admin.jpg",
// "header_static": "http://localhost:8080/header/admin.jpg",
// "emojis": []
// ]
// self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
// self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
// self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (ctx) in
// if ctx.captures[0] == "1" {
// return JSONResponse(result: selfAccount)
// } else {
// return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
// }
// }
// }
//
// func timelineRoutes() {
// let emptyTimeline: [Any] = []
// self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
// self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
// }
//
// func notificationRoutes() {
// let emptyTimeline: [Any] = []
// self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
// }
//}

View File

@ -7,23 +7,23 @@
//
import Foundation
import Ambassador
struct DelegatingResponse: WebApp {
let handler: (_ ctx: Context) -> WebApp
func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
let ctx = Context(environ: environ)
handler(ctx).app(environ, startResponse: startResponse, sendBody: sendBody)
}
}
extension DelegatingResponse {
struct Context {
let environ: [String: Any]
var captures: [String] {
environ["ambassador.router_captures"] as? [String] ?? []
}
}
}
//import Ambassador
//
//struct DelegatingResponse: WebApp {
// let handler: (_ ctx: Context) -> WebApp
//
// func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
// let ctx = Context(environ: environ)
// handler(ctx).app(environ, startResponse: startResponse, sendBody: sendBody)
// }
//}
//
//extension DelegatingResponse {
// struct Context {
// let environ: [String: Any]
//
// var captures: [String] {
// environ["ambassador.router_captures"] as? [String] ?? []
// }
// }
//}

View File

@ -7,12 +7,12 @@
//
import Foundation
import Ambassador
extension JSONResponse {
init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
return result
})
}
}
//import Ambassador
//
//extension JSONResponse {
// init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
// self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
// return result
// })
// }
//}

View File

@ -14,7 +14,7 @@ class ComposeTests: TuskerUITests {
override func setUp() {
super.setUp()
router.allRoutes()
// router.allRoutes()
app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
app.launch()

View File

@ -13,7 +13,7 @@ class MyProfileTests: TuskerUITests {
override func setUp() {
super.setUp()
router.allRoutes()
// router.allRoutes()
app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
app.launch()

View File

@ -7,14 +7,14 @@
//
import XCTest
import Ambassador
//import Ambassador
class OnboardingTests: TuskerUITests {
override func setUp() {
super.setUp()
router.instanceRoutes()
// router.instanceRoutes()
app.launch()
}

View File

@ -7,40 +7,40 @@
//
import XCTest
import Embassy
import Ambassador
//import Embassy
//import Ambassador
class TuskerUITests: XCTestCase {
var eventLoop: EventLoop!
var router: Router!
var server: HTTPServer!
var eventLoopThreadCondition: NSCondition!
var eventLoopThread: Thread!
// var eventLoop: EventLoop!
// var router: Router!
// var server: HTTPServer!
// var eventLoopThreadCondition: NSCondition!
// var eventLoopThread: Thread!
var app: XCUIApplication!
private func setupWebServer() {
eventLoop = try! SelectorEventLoop(selector: try! KqueueSelector())
router = Router()
server = DefaultHTTPServer(eventLoop: eventLoop, port: 8080, app: router.app)
router["/hello"] = JSONResponse(handler: { (_) in
return ["Hello", "World"]
})
try! server.start()
eventLoopThreadCondition = NSCondition()
eventLoopThread = Thread(block: {
self.eventLoop.runForever()
self.eventLoopThreadCondition.lock()
self.eventLoopThreadCondition.signal()
self.eventLoopThreadCondition.unlock()
})
eventLoopThread.start()
}
// private func setupWebServer() {
// eventLoop = try! SelectorEventLoop(selector: try! KqueueSelector())
// router = Router()
// server = DefaultHTTPServer(eventLoop: eventLoop, port: 8080, app: router.app)
// router["/hello"] = JSONResponse(handler: { (_) in
// return ["Hello", "World"]
// })
// try! server.start()
//
// eventLoopThreadCondition = NSCondition()
// eventLoopThread = Thread(block: {
// self.eventLoop.runForever()
// self.eventLoopThreadCondition.lock()
// self.eventLoopThreadCondition.signal()
// self.eventLoopThreadCondition.unlock()
// })
// eventLoopThread.start()
// }
override func setUp() {
setupWebServer()
// setupWebServer()
continueAfterFailure = false
app = XCUIApplication()
@ -48,14 +48,14 @@ class TuskerUITests: XCTestCase {
}
override func tearDown() {
server.stopAndWait()
eventLoopThreadCondition.lock()
eventLoop.stop()
while eventLoop.running {
if !eventLoopThreadCondition.wait(until: Date(timeIntervalSinceNow: 10)) {
fatalError("Join eventLoopThread timeout")
}
}
// server.stopAndWait()
// eventLoopThreadCondition.lock()
// eventLoop.stop()
// while eventLoop.running {
// if !eventLoopThreadCondition.wait(until: Date(timeIntervalSinceNow: 10)) {
// fatalError("Join eventLoopThread timeout")
// }
// }
}
}