ConcurrencyTests.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. //
  2. // ConcurrencyTests.swift
  3. // KeychainSwift
  4. //
  5. // Created by Eli Kohen on 08/02/2017.
  6. // Copyright © 2017 Marketplacer. All rights reserved.
  7. //
  8. import XCTest
  9. class ConcurrencyTests: XCTestCase {
  10. var obj: KeychainSwift!
  11. override func setUp() {
  12. super.setUp()
  13. obj = KeychainSwift()
  14. obj.clear()
  15. obj.lastQueryParameters = nil
  16. obj.synchronizable = false
  17. }
  18. // MARK: - addSynchronizableIfRequired
  19. func testConcurrencyDoesntCrash() {
  20. let expectation = self.expectation(description: "Wait for write loop")
  21. let dataToWrite = "{ asdf ñlk BNALSKDJFÑLAKSJDFÑLKJ ZÑCLXKJ ÑALSKDFJÑLKASJDFÑLKJASDÑFLKJAÑSDLKFJÑLKJ}"
  22. obj.set(dataToWrite, forKey: "test-key")
  23. var writes = 0
  24. let readQueue = DispatchQueue(label: "ReadQueue", attributes: [])
  25. readQueue.async {
  26. for _ in 0..<400 {
  27. let _: String? = synchronize( { completion in
  28. let result: String? = self.obj.get("test-key")
  29. DispatchQueue.global(qos: .background).async {
  30. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  31. completion(result)
  32. }
  33. }
  34. }, timeoutWith: nil)
  35. }
  36. }
  37. let readQueue2 = DispatchQueue(label: "ReadQueue2", attributes: [])
  38. readQueue2.async {
  39. for _ in 0..<400 {
  40. let _: String? = synchronize( { completion in
  41. let result: String? = self.obj.get("test-key")
  42. DispatchQueue.global(qos: .background).async {
  43. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  44. completion(result)
  45. }
  46. }
  47. }, timeoutWith: nil)
  48. }
  49. }
  50. let readQueue3 = DispatchQueue(label: "ReadQueue3", attributes: [])
  51. readQueue3.async {
  52. for _ in 0..<400 {
  53. let _: String? = synchronize( { completion in
  54. let result: String? = self.obj.get("test-key")
  55. DispatchQueue.global(qos: .background).async {
  56. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  57. completion(result)
  58. }
  59. }
  60. }, timeoutWith: nil)
  61. }
  62. }
  63. let writeQueue = DispatchQueue(label: "WriteQueue", attributes: [])
  64. writeQueue.async {
  65. for _ in 0..<500 {
  66. let written: Bool = synchronize({ completion in
  67. DispatchQueue.global(qos: .background).async {
  68. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  69. let result = self.obj.set(dataToWrite, forKey: "test-key")
  70. completion(result)
  71. }
  72. }
  73. }, timeoutWith: false)
  74. if written {
  75. writes = writes + 1
  76. }
  77. }
  78. expectation.fulfill()
  79. }
  80. for _ in 0..<1000 {
  81. self.obj.set(dataToWrite, forKey: "test-key")
  82. let _ = self.obj.get("test-key")
  83. }
  84. self.waitForExpectations(timeout: 20, handler: nil)
  85. XCTAssertEqual(500, writes)
  86. }
  87. }
  88. // Synchronizes a asynch closure
  89. // Ref: https://forums.developer.apple.com/thread/11519
  90. func synchronize<ResultType>(_ asynchClosure: (_ completion: @escaping (ResultType) -> ()) -> Void,
  91. timeout: DispatchTime = DispatchTime.distantFuture, timeoutWith: @autoclosure @escaping () -> ResultType) -> ResultType {
  92. let sem = DispatchSemaphore(value: 0)
  93. var result: ResultType?
  94. asynchClosure { (r: ResultType) -> () in
  95. result = r
  96. sem.signal()
  97. }
  98. _ = sem.wait(timeout: timeout)
  99. if result == nil {
  100. result = timeoutWith()
  101. }
  102. return result!
  103. }