Skip to content

Commit 2a7868a

Browse files
test: Fix potential deadlock in TestLogOutput (#5617)
The test testLoggingFromMultipleThreads lead to deadlocks. This is fixed now by using a NSLock instead of a DispatchQueue. This PR also makes the logsToConsole immutable and adds more tests.
1 parent 42cfd79 commit 2a7868a

File tree

1 file changed

+111
-14
lines changed

1 file changed

+111
-14
lines changed

Tests/SentryTests/TestLogOutput.swift

Lines changed: 111 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import XCTest
44

55
final class TestLogOutput {
66

7-
private let queue = DispatchQueue(label: "TestLogOutput", attributes: .concurrent)
8-
7+
private let logsToConsole: Bool
98
private var _loggedMessages: [String] = []
10-
11-
var logsToConsole: Bool = true
9+
private let lock = NSLock()
10+
11+
init(logsToConsole: Bool = true) {
12+
self.logsToConsole = logsToConsole
13+
}
1214

1315
var loggedMessages: [String] {
14-
queue.sync {
16+
lock.synchronized {
1517
return _loggedMessages
1618
}
1719
}
@@ -20,21 +22,116 @@ final class TestLogOutput {
2022
if logsToConsole {
2123
print(message)
2224
}
23-
queue.async(flags: .barrier) {
25+
lock.synchronized {
2426
self._loggedMessages.append(message)
2527
}
2628
}
2729
}
2830

2931
class TestLogOutPutTests: XCTestCase {
3032

31-
func testLoggingFromMulitpleThreads() {
32-
let sut = TestLogOutput()
33-
sut.logsToConsole = false
34-
testConcurrentModifications(writeWork: { i in
35-
sut.log("Some message \(i)")
36-
}, readWork: {
37-
XCTAssertNotNil(sut.loggedMessages)
38-
})
33+
func testLoggingFromMultipleThreads() {
34+
// Arrange
35+
let sut = TestLogOutput(logsToConsole: false)
36+
37+
let queue = DispatchQueue(label: "testLoggingFromMultipleThreads", attributes: [.concurrent])
38+
let expectation = expectation(description: "Logging from multiple threads")
39+
40+
let iterations = 100
41+
expectation.expectedFulfillmentCount = iterations * 2
42+
43+
// Act
44+
for i in 0..<iterations {
45+
46+
queue.async {
47+
sut.log("Message \(i)")
48+
expectation.fulfill()
49+
}
50+
51+
queue.async {
52+
XCTAssertNotNil(sut.loggedMessages)
53+
expectation.fulfill()
54+
}
55+
}
56+
57+
wait(for: [expectation], timeout: 5.0)
58+
59+
// Assert
60+
XCTAssertEqual(sut.loggedMessages.count, iterations)
61+
}
62+
63+
func testInitialState_LoggedMessagesIsEmpty() {
64+
// Arrange
65+
let sut = TestLogOutput(logsToConsole: false)
66+
67+
// Act
68+
let messages = sut.loggedMessages
69+
70+
// Assert
71+
XCTAssertTrue(messages.isEmpty)
72+
XCTAssertEqual(messages.count, 0)
73+
}
74+
75+
func testLogSingleMessage_MessageIsStored() {
76+
// Arrange
77+
let sut = TestLogOutput(logsToConsole: false)
78+
let testMessage = "Test message"
79+
80+
// Act
81+
sut.log(testMessage)
82+
83+
// Assert
84+
let messages = sut.loggedMessages
85+
XCTAssertEqual(messages.count, 1)
86+
XCTAssertEqual(messages.first, testMessage)
87+
}
88+
89+
func testLogMultipleMessages_AllMessagesAreStoredInOrder() {
90+
// Arrange
91+
let sut = TestLogOutput(logsToConsole: false)
92+
let testMessages = ["First message", "Second message", "Third message"]
93+
94+
// Act
95+
for message in testMessages {
96+
sut.log(message)
97+
}
98+
99+
// Assert
100+
let messages = sut.loggedMessages
101+
XCTAssertEqual(messages.count, testMessages.count)
102+
XCTAssertEqual(messages, testMessages)
103+
}
104+
105+
func testLogEmptyString_EmptyStringIsStored() {
106+
// Arrange
107+
let sut = TestLogOutput(logsToConsole: false)
108+
let emptyMessage = ""
109+
110+
// Act
111+
sut.log(emptyMessage)
112+
113+
// Assert
114+
let messages = sut.loggedMessages
115+
XCTAssertEqual(messages.count, 1)
116+
XCTAssertEqual(messages.first, emptyMessage)
117+
}
118+
119+
func testLogAfterMultipleReads_MessagesAreConsistent() {
120+
// Arrange
121+
let sut = TestLogOutput(logsToConsole: false)
122+
let firstMessage = "First message"
123+
let secondMessage = "Second message"
124+
125+
// Act
126+
sut.log(firstMessage)
127+
let messagesAfterFirst = sut.loggedMessages
128+
sut.log(secondMessage)
129+
let messagesAfterSecond = sut.loggedMessages
130+
131+
// Assert
132+
XCTAssertEqual(messagesAfterFirst.count, 1)
133+
XCTAssertEqual(messagesAfterFirst.first, firstMessage)
134+
XCTAssertEqual(messagesAfterSecond.count, 2)
135+
XCTAssertEqual(messagesAfterSecond, [firstMessage, secondMessage])
39136
}
40137
}

0 commit comments

Comments
 (0)