Skip to content

Commit 98f11ae

Browse files
Property wrapper (#19)
1. Мигрировал тесты на Swift Testing 2. Добавил property wrapper для более удобного использования
1 parent fae557b commit 98f11ae

File tree

3 files changed

+275
-49
lines changed

3 files changed

+275
-49
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
3+
@propertyWrapper
4+
public final class ThreadSafe<T> {
5+
private let container: DBThreadSafeContainer<T>
6+
7+
public init(wrappedValue: T) {
8+
self.container = DBThreadSafeContainer(wrappedValue)
9+
}
10+
11+
public var wrappedValue: T {
12+
get {
13+
container.read()
14+
}
15+
@available(
16+
*,
17+
unavailable,
18+
message: "Use $property.write { } to modify the value"
19+
)
20+
set {
21+
container.write(newValue)
22+
}
23+
}
24+
25+
public var projectedValue: DBThreadSafeContainer<T> {
26+
container
27+
}
28+
}
Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,129 @@
11
import DBThreadSafe
2-
import XCTest
2+
import Foundation
3+
import Testing
34

4-
class DBThreadSafeContainerTests: XCTestCase {
5+
@Suite("DBThreadSafeContainer Tests")
6+
struct DBThreadSafeContainerTests {
57
let iterations = 100000
6-
7-
func testConcurrentGet() {
8+
9+
@Test("Concurrent reads return correct value")
10+
func concurrentGet() {
811
let container = DBThreadSafeContainer(0)
9-
12+
1013
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
1114
_ = container.read()
1215
}
13-
14-
XCTAssertEqual(container.read(), 0)
16+
17+
#expect(container.read() == 0)
1518
}
16-
17-
func testRead() {
19+
20+
@Test("Read with closure")
21+
func read() {
1822
let container = DBThreadSafeContainer("Hello, World!")
19-
23+
2024
enum TestError: Error {
2125
case someError
2226
}
23-
27+
2428
// Test case 1: Read value successfully
2529
let expectedValue1 = "Hello, World!"
2630
container.read { value in
27-
XCTAssertEqual(value, expectedValue1)
31+
#expect(value == expectedValue1)
2832
}
29-
33+
3034
// Test case 2: Read value with throwing closure
31-
XCTAssertThrowsError(try container.read { _ in
32-
throw TestError.someError
33-
}) { error in
34-
XCTAssertEqual(error as? TestError, TestError.someError)
35+
#expect(throws: TestError.self) {
36+
try container.read { _ in
37+
throw TestError.someError
38+
}
3539
}
3640
}
37-
38-
func testReadClosureReturnValue() {
41+
42+
@Test("Read closure with return value")
43+
func readClosureReturnValue() {
3944
let container = DBThreadSafeContainer("Hello, World!")
40-
45+
4146
let result = container.read { $0.count }
42-
43-
XCTAssertEqual(result, 13)
47+
48+
#expect(result == 13)
4449
}
4550

46-
47-
func testConcurrentSet() {
51+
52+
@Test("Concurrent writes increment correctly")
53+
func concurrentSet() {
4854
let container = DBThreadSafeContainer(0)
49-
55+
5056
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
5157
container.write { value in
5258
let newValue = value + 1
5359
value = newValue
5460
}
5561
}
56-
57-
XCTAssertEqual(container.read(), iterations)
62+
63+
#expect(container.read() == iterations)
5864
}
59-
60-
func testConcurrentGetArray() {
65+
66+
@Test("Concurrent array reads return correct value")
67+
func concurrentGetArray() {
6168
let container = DBThreadSafeContainer([1, 2, 3])
62-
69+
6370
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
6471
_ = container.read()
6572
}
66-
67-
XCTAssertEqual(container.read(), [1, 2, 3])
73+
74+
#expect(container.read() == [1, 2, 3])
6875
}
69-
70-
func testConcurrentSetArray() throws {
76+
77+
@Test("Concurrent array appends")
78+
func concurrentSetArray() throws {
7179
let container = DBThreadSafeContainer([0])
72-
80+
7381
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
7482
container.write { value in
7583
let lastValue = value.last!
7684
value.append(lastValue + 1)
7785
}
7886
}
79-
80-
XCTAssertEqual(container.read().last, iterations)
87+
88+
#expect(container.read().last == iterations)
8189
}
82-
83-
func testConcurrentGetDictionary() {
90+
91+
@Test("Concurrent dictionary reads return correct value")
92+
func concurrentGetDictionary() {
8493
let container = DBThreadSafeContainer(["key1": "value1", "key2": "value2"])
85-
94+
8695
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
8796
_ = container.read()
8897
}
89-
90-
XCTAssertEqual(container.read(), ["key1": "value1", "key2": "value2"])
98+
99+
#expect(container.read() == ["key1": "value1", "key2": "value2"])
91100
}
92-
93-
func testConcurrentSetDictionary() {
101+
102+
@Test("Concurrent dictionary operations")
103+
func concurrentSetDictionary() {
94104
let container = DBThreadSafeContainer(["key": 0])
95-
105+
96106
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
97107
container.write { dict in
98108
let value = dict["key"]
99109
dict["key"] = value! + 1
100110
}
101111
}
102-
112+
103113
DispatchQueue.concurrentPerform(iterations: iterations) { i in
104114
container.write { dict in
105115
let key = "key\(i)"
106116
dict[key] = i
107117
}
108118
}
109-
119+
110120
DispatchQueue.concurrentPerform(iterations: iterations) { i in
111121
container.write { dict in
112122
let key = "key\(i)"
113123
dict.removeValue(forKey: key)
114124
}
115125
}
116-
117-
XCTAssertEqual(container.read(), ["key": iterations])
126+
127+
#expect(container.read() == ["key": iterations])
118128
}
119129
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import DBThreadSafe
2+
import Foundation
3+
import Testing
4+
5+
@Suite("ThreadSafe Property Wrapper Tests")
6+
struct ThreadSafeTests {
7+
let iterations = 100000
8+
9+
@Test("Reading wrappedValue returns correct value")
10+
func readWrappedValue() {
11+
@ThreadSafe var counter = 42
12+
13+
#expect(counter == 42)
14+
}
15+
16+
@Test("Writing with write closure updates value")
17+
func writeWithClosure() {
18+
@ThreadSafe var counter = 0
19+
20+
$counter.write { value in
21+
value = 100
22+
}
23+
24+
#expect(counter == 100)
25+
}
26+
27+
@Test("Concurrent reads via wrappedValue")
28+
func concurrentReads() {
29+
@ThreadSafe var value = 42
30+
31+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
32+
_ = value
33+
}
34+
35+
#expect(value == 42)
36+
}
37+
38+
@Test("Concurrent writes with write closure")
39+
func concurrentWrites() {
40+
@ThreadSafe var counter = 0
41+
42+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
43+
$counter.write { value in
44+
value += 1
45+
}
46+
}
47+
48+
#expect(counter == iterations)
49+
}
50+
51+
@Test("Projected value provides access to container")
52+
func projectedValue() {
53+
@ThreadSafe var counter = 0
54+
55+
$counter.write { value in
56+
value = 100
57+
}
58+
59+
#expect(counter == 100)
60+
}
61+
62+
@Test("Concurrent writes using projected value write closure")
63+
func concurrentWritesWithClosure() {
64+
@ThreadSafe var counter = 0
65+
66+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
67+
$counter.write { value in
68+
value += 1
69+
}
70+
}
71+
72+
#expect(counter == iterations)
73+
}
74+
75+
@Test("Reading with projected value read method")
76+
func readWithProjectedValue() {
77+
@ThreadSafe var text = "Hello, World!"
78+
79+
let length = $text.read { $0.count }
80+
81+
#expect(length == 13)
82+
}
83+
84+
@Test("Concurrent array operations via projected value")
85+
func concurrentArrayOperations() {
86+
@ThreadSafe var numbers = [0]
87+
88+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
89+
$numbers.write { value in
90+
let lastValue = value.last!
91+
value.append(lastValue + 1)
92+
}
93+
}
94+
95+
#expect(numbers.last == iterations)
96+
}
97+
98+
@Test("Concurrent dictionary operations via projected value")
99+
func concurrentDictionaryOperations() {
100+
@ThreadSafe var dict = ["key": 0]
101+
102+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
103+
$dict.write { dict in
104+
let value = dict["key"]
105+
dict["key"] = value! + 1
106+
}
107+
}
108+
109+
#expect(dict["key"] == iterations)
110+
}
111+
112+
@Test("Struct property with ThreadSafe wrapper")
113+
func structProperty() {
114+
struct Counter {
115+
@ThreadSafe var value = 0
116+
}
117+
118+
let counter = Counter()
119+
counter.$value.write { value in
120+
value = 50
121+
}
122+
123+
#expect(counter.value == 50)
124+
}
125+
126+
@Test("Class property with ThreadSafe wrapper")
127+
func classProperty() {
128+
class Counter {
129+
@ThreadSafe var value = 0
130+
}
131+
132+
let counter = Counter()
133+
counter.$value.write { value in
134+
value = 75
135+
}
136+
137+
#expect(counter.value == 75)
138+
139+
counter.$value.write { value in
140+
value = 100
141+
}
142+
143+
#expect(counter.value == 100)
144+
}
145+
146+
@Test("write method with throwing closure")
147+
func writeThrowing() {
148+
@ThreadSafe var counter = 0
149+
150+
enum TestError: Error {
151+
case someError
152+
}
153+
154+
#expect(throws: TestError.self) {
155+
try $counter.write { _ in
156+
throw TestError.someError
157+
}
158+
}
159+
}
160+
161+
@Test("Concurrent array operations via write closure")
162+
func concurrentArrayWithWrite() {
163+
@ThreadSafe var numbers = [0]
164+
165+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
166+
$numbers.write { value in
167+
let lastValue = value.last!
168+
value.append(lastValue + 1)
169+
}
170+
}
171+
172+
#expect(numbers.last == iterations)
173+
}
174+
175+
@Test("Concurrent dictionary operations via write closure")
176+
func concurrentDictionaryWithWrite() {
177+
@ThreadSafe var dict = ["key": 0]
178+
179+
DispatchQueue.concurrentPerform(iterations: iterations) { _ in
180+
$dict.write { dict in
181+
let value = dict["key"]
182+
dict["key"] = value! + 1
183+
}
184+
}
185+
186+
#expect(dict["key"] == iterations)
187+
}
188+
}

0 commit comments

Comments
 (0)