Skip to content

Commit 97d2755

Browse files
committed
Add unit tests for Singleton, Adapter, Facade, and HashAlgorithm
- Add SingletonTests.swift with tests for ThreadSafeSingleton and ActorSingleton - Add AdapterTests.swift with comprehensive adapter pattern tests - Add FacadeTests.swift with facade pattern tests including async support - Add HashAlgorithmTests.swift with SHA-256 hash algorithm tests
1 parent 8d3b3b4 commit 97d2755

4 files changed

Lines changed: 801 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//
2+
// HashAlgorithmTests.swift
3+
// DesignAlgorithmsKitTests
4+
//
5+
// Unit tests for Hash Algorithm
6+
//
7+
8+
import XCTest
9+
@testable import DesignAlgorithmsKit
10+
11+
final class HashAlgorithmTests: XCTestCase {
12+
13+
// MARK: - SHA256 Tests
14+
15+
func testSHA256Name() {
16+
// Then
17+
XCTAssertEqual(SHA256.name, "SHA-256")
18+
}
19+
20+
func testSHA256HashData() {
21+
// Given
22+
let data = "Hello, World!".data(using: .utf8)!
23+
24+
// When
25+
let hash1 = SHA256.hash(data: data)
26+
let hash2 = SHA256.hash(data: data)
27+
28+
// Then
29+
XCTAssertEqual(hash1.count, 32, "SHA-256 should produce 32 bytes")
30+
XCTAssertEqual(hash2.count, 32, "SHA-256 should produce 32 bytes")
31+
XCTAssertEqual(hash1, hash2, "Same input should produce same hash")
32+
}
33+
34+
func testSHA256HashString() {
35+
// Given
36+
let string = "Hello, World!"
37+
38+
// When
39+
let hash1 = SHA256.hash(string: string)
40+
let hash2 = SHA256.hash(string: string)
41+
42+
// Then
43+
XCTAssertEqual(hash1.count, 32, "SHA-256 should produce 32 bytes")
44+
XCTAssertEqual(hash2.count, 32, "SHA-256 should produce 32 bytes")
45+
XCTAssertEqual(hash1, hash2, "Same input should produce same hash")
46+
}
47+
48+
func testSHA256HashDifferentInputs() {
49+
// Given
50+
let data1 = "Hello".data(using: .utf8)!
51+
let data2 = "World".data(using: .utf8)!
52+
53+
// When
54+
let hash1 = SHA256.hash(data: data1)
55+
let hash2 = SHA256.hash(data: data2)
56+
57+
// Then
58+
XCTAssertNotEqual(hash1, hash2, "Different inputs should produce different hashes")
59+
}
60+
61+
func testSHA256HashEmptyData() {
62+
// Given
63+
let emptyData = Data()
64+
65+
// When
66+
let hash = SHA256.hash(data: emptyData)
67+
68+
// Then
69+
XCTAssertEqual(hash.count, 32, "Empty data should still produce 32-byte hash")
70+
}
71+
72+
func testSHA256HashEmptyString() {
73+
// Given
74+
let emptyString = ""
75+
76+
// When
77+
let hash = SHA256.hash(string: emptyString)
78+
79+
// Then
80+
XCTAssertEqual(hash.count, 32, "Empty string should still produce 32-byte hash")
81+
}
82+
83+
func testSHA256HashLargeData() {
84+
// Given
85+
let largeData = Data(repeating: 0x42, count: 10000)
86+
87+
// When
88+
let hash = SHA256.hash(data: largeData)
89+
90+
// Then
91+
XCTAssertEqual(hash.count, 32, "Large data should produce 32-byte hash")
92+
}
93+
94+
func testSHA256HashConsistency() {
95+
// Given
96+
let testCases = [
97+
"test",
98+
"Hello, World!",
99+
"1234567890",
100+
"The quick brown fox jumps over the lazy dog",
101+
"Special chars: !@#$%^&*()",
102+
"Unicode: 🚀🌟✨",
103+
"Multi\nline\nstring"
104+
]
105+
106+
// When/Then
107+
for testCase in testCases {
108+
let hash1 = SHA256.hash(string: testCase)
109+
let hash2 = SHA256.hash(string: testCase)
110+
XCTAssertEqual(hash1, hash2, "Hash should be consistent for: \(testCase)")
111+
XCTAssertEqual(hash1.count, 32, "Hash should be 32 bytes for: \(testCase)")
112+
}
113+
}
114+
115+
func testSHA256HashUnicodeString() {
116+
// Given
117+
let unicodeStrings = [
118+
"Hello, 世界",
119+
"مرحبا",
120+
"Здравствуй",
121+
"こんにちは",
122+
"🚀🌟✨"
123+
]
124+
125+
// When/Then
126+
for string in unicodeStrings {
127+
let hash = SHA256.hash(string: string)
128+
XCTAssertEqual(hash.count, 32, "Unicode string should produce 32-byte hash: \(string)")
129+
}
130+
}
131+
132+
// MARK: - HashAlgorithm Protocol Tests
133+
134+
func testHashAlgorithmProtocolConformance() {
135+
// Then
136+
XCTAssertEqual(SHA256.name, "SHA-256")
137+
138+
let testData = "test".data(using: .utf8)!
139+
let hash = SHA256.hash(data: testData)
140+
XCTAssertEqual(hash.count, 32)
141+
}
142+
143+
func testHashAlgorithmStringExtension() {
144+
// Given
145+
let string = "test string"
146+
147+
// When
148+
let hashFromString = SHA256.hash(string: string)
149+
let hashFromData = SHA256.hash(data: string.data(using: .utf8)!)
150+
151+
// Then
152+
XCTAssertEqual(hashFromString, hashFromData, "String extension should produce same hash as data")
153+
}
154+
155+
func testHashAlgorithmInvalidUTF8() {
156+
// Given
157+
// Create data that can't be converted to UTF-8 string
158+
let invalidUTF8Data = Data([0xFF, 0xFE, 0xFD])
159+
160+
// When
161+
let hash = SHA256.hash(data: invalidUTF8Data)
162+
163+
// Then
164+
XCTAssertEqual(hash.count, 32, "Invalid UTF-8 data should still produce hash")
165+
}
166+
}
167+
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
//
2+
// SingletonTests.swift
3+
// DesignAlgorithmsKitTests
4+
//
5+
// Unit tests for Singleton Pattern
6+
//
7+
8+
import XCTest
9+
@testable import DesignAlgorithmsKit
10+
11+
final class SingletonTests: XCTestCase {
12+
13+
// MARK: - ThreadSafeSingleton Tests
14+
15+
func testThreadSafeSingletonSingleInstance() {
16+
// Given - Using a unique class name to avoid static storage conflicts
17+
class UniqueTestSingleton1: ThreadSafeSingleton {
18+
var value: String = "initial"
19+
20+
private override init() {
21+
super.init()
22+
}
23+
24+
override class func createShared() -> Self {
25+
// Use a unique static storage per class
26+
struct StaticStorage {
27+
static var instance: UniqueTestSingleton1?
28+
}
29+
if StaticStorage.instance == nil {
30+
StaticStorage.instance = UniqueTestSingleton1()
31+
}
32+
return StaticStorage.instance! as! Self
33+
}
34+
}
35+
36+
// When
37+
let instance1 = UniqueTestSingleton1.shared
38+
let instance2 = UniqueTestSingleton1.shared
39+
40+
// Then
41+
XCTAssertTrue(instance1 === instance2, "Should return the same instance")
42+
XCTAssertEqual(instance1.value, "initial")
43+
}
44+
45+
func testThreadSafeSingletonThreadSafety() {
46+
// Given
47+
class TestSingleton: ThreadSafeSingleton {
48+
var counter: Int = 0
49+
50+
private override init() {
51+
super.init()
52+
}
53+
54+
override class func createShared() -> Self {
55+
return TestSingleton() as! Self
56+
}
57+
58+
func increment() {
59+
counter += 1
60+
}
61+
}
62+
63+
// When - Access from multiple threads
64+
let expectation = expectation(description: "Thread safety test")
65+
expectation.expectedFulfillmentCount = 10
66+
67+
for _ in 0..<10 {
68+
DispatchQueue.global().async {
69+
let instance = TestSingleton.shared
70+
instance.increment()
71+
expectation.fulfill()
72+
}
73+
}
74+
75+
waitForExpectations(timeout: 2.0)
76+
77+
// Then - Should still be the same instance
78+
let instance1 = TestSingleton.shared
79+
let instance2 = TestSingleton.shared
80+
XCTAssertTrue(instance1 === instance2, "Should return the same instance across threads")
81+
}
82+
83+
func testThreadSafeSingletonState() {
84+
// Given
85+
class TestSingleton: ThreadSafeSingleton {
86+
var state: String = "initial"
87+
88+
private override init() {
89+
super.init()
90+
}
91+
92+
override class func createShared() -> Self {
93+
return TestSingleton() as! Self
94+
}
95+
}
96+
97+
// When
98+
let instance = TestSingleton.shared
99+
instance.state = "modified"
100+
101+
// Then
102+
let instance2 = TestSingleton.shared
103+
XCTAssertEqual(instance2.state, "modified", "State should persist across accesses")
104+
}
105+
106+
func testThreadSafeSingletonSubclass() {
107+
// Given
108+
class BaseSingleton: ThreadSafeSingleton {
109+
var baseValue: String = "base"
110+
111+
private override init() {
112+
super.init()
113+
}
114+
115+
override class func createShared() -> Self {
116+
return BaseSingleton() as! Self
117+
}
118+
}
119+
120+
class DerivedSingleton: ThreadSafeSingleton {
121+
var derivedValue: String = "derived"
122+
123+
private override init() {
124+
super.init()
125+
}
126+
127+
override class func createShared() -> Self {
128+
return DerivedSingleton() as! Self
129+
}
130+
}
131+
132+
// When
133+
let base = BaseSingleton.shared
134+
let derived = DerivedSingleton.shared
135+
136+
// Then
137+
XCTAssertNotNil(base)
138+
XCTAssertNotNil(derived)
139+
XCTAssertTrue(type(of: base) == BaseSingleton.self)
140+
XCTAssertTrue(type(of: derived) == DerivedSingleton.self)
141+
}
142+
143+
// MARK: - Singleton Protocol Tests
144+
145+
func testSingletonProtocol() {
146+
// Given
147+
class ProtocolSingleton: ThreadSafeSingleton, Singleton {
148+
private override init() {
149+
super.init()
150+
}
151+
152+
override class func createShared() -> Self {
153+
return ProtocolSingleton() as! Self
154+
}
155+
}
156+
157+
// When
158+
let instance1 = ProtocolSingleton.shared
159+
let instance2 = ProtocolSingleton.shared
160+
161+
// Then
162+
XCTAssertTrue(instance1 === instance2, "Should conform to Singleton protocol")
163+
}
164+
165+
// MARK: - ActorSingleton Protocol Tests
166+
167+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
168+
func testActorSingletonProtocol() {
169+
// Given
170+
actor TestActorSingleton: ActorSingleton {
171+
static let shared = TestActorSingleton()
172+
173+
private init() {}
174+
175+
var value: String = "initial"
176+
177+
func setValue(_ newValue: String) {
178+
value = newValue
179+
}
180+
181+
func getValue() -> String {
182+
return value
183+
}
184+
}
185+
186+
// When/Then
187+
let expectation = expectation(description: "Actor singleton test")
188+
189+
Task {
190+
let instance1 = TestActorSingleton.shared
191+
let instance2 = TestActorSingleton.shared
192+
193+
await instance1.setValue("modified")
194+
let value1 = await instance1.getValue()
195+
let value2 = await instance2.getValue()
196+
197+
XCTAssertEqual(value1, "modified")
198+
XCTAssertEqual(value2, "modified", "Actor singleton should share state")
199+
200+
expectation.fulfill()
201+
}
202+
203+
waitForExpectations(timeout: 2.0)
204+
}
205+
}
206+

0 commit comments

Comments
 (0)