mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
- UnitTests: Added unitttests for new datastructures - LinkedHashMap and TreeMap
This commit is contained in:
@@ -59,12 +59,18 @@
|
||||
A80D60D32D3DD85100CEF65D /* ReleaseTrack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */; };
|
||||
A80D790D2D2F20AF00A40F40 /* PaginationIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */; };
|
||||
A80D790F2D2F217000A40F40 /* PaginationDataHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */; };
|
||||
A81A8CB52D68B2180086C96F /* TreeMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB42D68B2180086C96F /* TreeMapTests.swift */; };
|
||||
A81A8CB92D68B30B0086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
||||
A81A8CBA2D68B3110086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
||||
A81A8CBB2D68B3230086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
||||
A81A8CBD2D68B43F0086C96F /* LinkedHashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */; };
|
||||
A81A8CBE2D68B43F0086C96F /* LinkedHashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */; };
|
||||
A81A8CC02D68B4520086C96F /* LinkedHashMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBF2D68B4520086C96F /* LinkedHashMapTests.swift */; };
|
||||
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; };
|
||||
A82067C42D03E0DE00645C0D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = A82067C32D03E0DE00645C0D /* SemanticVersion */; };
|
||||
A859ED5C2D1EE827003DCC58 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; };
|
||||
A859ED5D2D1EE827003DCC58 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
A86315DF2D3EB2DE0048FA40 /* ErrorProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */; };
|
||||
A868CFE42D31999A002F1201 /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
||||
A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; };
|
||||
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; };
|
||||
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; };
|
||||
@@ -650,6 +656,10 @@
|
||||
A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack.swift; sourceTree = "<group>"; };
|
||||
A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIntent.swift; sourceTree = "<group>"; };
|
||||
A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataHolder.swift; sourceTree = "<group>"; };
|
||||
A81A8CB02D68B0320086C96F /* TreeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMap.swift; sourceTree = "<group>"; };
|
||||
A81A8CB42D68B2180086C96F /* TreeMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMapTests.swift; sourceTree = "<group>"; };
|
||||
A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedHashMap.swift; sourceTree = "<group>"; };
|
||||
A81A8CBF2D68B4520086C96F /* LinkedHashMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedHashMapTests.swift; sourceTree = "<group>"; };
|
||||
A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = SideStore/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
|
||||
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = "<group>"; };
|
||||
A85ACB8F2D1F31C400AA3DE7 /* AltStore.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStore.debug.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -1227,6 +1237,23 @@
|
||||
path = Intents;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A81A8CB22D68B2030086C96F /* UnitTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A81A8CB32D68B20F0086C96F /* datastructures */,
|
||||
);
|
||||
path = UnitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A81A8CB32D68B20F0086C96F /* datastructures */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A81A8CBF2D68B4520086C96F /* LinkedHashMapTests.swift */,
|
||||
A81A8CB42D68B2180086C96F /* TreeMapTests.swift */,
|
||||
);
|
||||
path = datastructures;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A85ACB942D1F31C400AA3DE7 /* xcconfigs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1289,6 +1316,8 @@
|
||||
A8AD35572D31BEB2003A28B4 /* datastructures */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */,
|
||||
A81A8CB02D68B0320086C96F /* TreeMap.swift */,
|
||||
A868CFE32D319988002F1201 /* SingletonGenericMap.swift */,
|
||||
);
|
||||
path = datastructures;
|
||||
@@ -1357,6 +1386,7 @@
|
||||
A8E2DB352D6850A9009E5D31 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A81A8CB22D68B2030086C96F /* UnitTests */,
|
||||
A8E2DB302D684E2A009E5D31 /* UITests */,
|
||||
A8E2DB332D68507F009E5D31 /* SideStoreTests.xctestplan */,
|
||||
);
|
||||
@@ -2895,7 +2925,11 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A8E2DB312D684E2A009E5D31 /* UITests.swift in Sources */,
|
||||
A81A8CB52D68B2180086C96F /* TreeMapTests.swift in Sources */,
|
||||
A81A8CBB2D68B3230086C96F /* TreeMap.swift in Sources */,
|
||||
A8E2DB322D684E2A009E5D31 /* UITestsLaunchTests.swift in Sources */,
|
||||
A81A8CC02D68B4520086C96F /* LinkedHashMapTests.swift in Sources */,
|
||||
A81A8CBE2D68B43F0086C96F /* LinkedHashMap.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3100,6 +3134,7 @@
|
||||
files = (
|
||||
D55467C52A8D72C300F4CE90 /* ActiveAppsWidget.swift in Sources */,
|
||||
D577AB7B2A967DF5007FE952 /* AppsTimelineProvider.swift in Sources */,
|
||||
A81A8CB92D68B30B0086C96F /* SingletonGenericMap.swift in Sources */,
|
||||
D577AB7F2A96878A007FE952 /* AppDetailWidget.swift in Sources */,
|
||||
BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */,
|
||||
D5151BE22A90363300C96F28 /* RefreshAllAppsWidgetIntent.swift in Sources */,
|
||||
@@ -3108,7 +3143,6 @@
|
||||
D5FD4EC92A9530C00097BEE8 /* AppSnapshot.swift in Sources */,
|
||||
A8AD35592D31BF2C003A28B4 /* PageInfoManager.swift in Sources */,
|
||||
D5151BE72A90395400C96F28 /* View+AltWidget.swift in Sources */,
|
||||
A868CFE42D31999A002F1201 /* SingletonGenericMap.swift in Sources */,
|
||||
A8A853AF2D3065A300995795 /* ActiveAppsTimelineProvider.swift in Sources */,
|
||||
BF98917F250AAC4F002ACF50 /* LockScreenWidget.swift in Sources */,
|
||||
A800F7042CE28E3800208744 /* View+AltWidget.swift in Sources */,
|
||||
@@ -3142,6 +3176,7 @@
|
||||
D5E1E7C128077DE90016FC96 /* UpdateKnownSourcesOperation.swift in Sources */,
|
||||
BFE338DF22F0EADB002E24B9 /* FetchSourceOperation.swift in Sources */,
|
||||
D54DED1428CBC44B008B27A0 /* ErrorLogTableViewCell.swift in Sources */,
|
||||
A81A8CBD2D68B43F0086C96F /* LinkedHashMap.swift in Sources */,
|
||||
D5390C3C2AC3A43900D17E62 /* AddSourceViewController.swift in Sources */,
|
||||
D5CD805F29CA755E00E591B0 /* SourceDetailViewController.swift in Sources */,
|
||||
D57968CB29CB99EF00539069 /* VibrantButton.swift in Sources */,
|
||||
@@ -3224,6 +3259,7 @@
|
||||
BF770E5122BB1CF6002A40FE /* InstallAppOperation.swift in Sources */,
|
||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
||||
A8FD917B2D0472DD00322782 /* DeprecatedAPIs.swift in Sources */,
|
||||
A81A8CBA2D68B3110086C96F /* TreeMap.swift in Sources */,
|
||||
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
|
||||
A8C38C382D2084D000E83DBD /* ConsoleLogView.swift in Sources */,
|
||||
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"testTargets" : [
|
||||
{
|
||||
"skippedTests" : [
|
||||
"UITests",
|
||||
"UITests\/testBulkAddRecommendedSources()",
|
||||
"UITests\/testLaunchPerformance()",
|
||||
"UITestsLaunchTests",
|
||||
"UITestsLaunchTests\/testLaunch()"
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// LinkedHashMapTests.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Magesh K on 21/02/25.
|
||||
// Copyright © 2025 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
// A helper class that signals when it is deallocated.
|
||||
class LeakTester {
|
||||
let id: Int
|
||||
var onDeinit: (() -> Void)?
|
||||
init(id: Int, onDeinit: (() -> Void)? = nil) {
|
||||
self.id = id
|
||||
self.onDeinit = onDeinit
|
||||
}
|
||||
deinit {
|
||||
onDeinit?()
|
||||
}
|
||||
}
|
||||
|
||||
final class LinkedHashMapTests: XCTestCase {
|
||||
|
||||
// Test that insertion preserves order and that iteration returns items in insertion order.
|
||||
func testInsertionAndOrder() {
|
||||
let map = LinkedHashMap<String, Int>()
|
||||
map.put(key: "one", value: 1)
|
||||
map.put(key: "two", value: 2)
|
||||
map.put(key: "three", value: 3)
|
||||
|
||||
XCTAssertEqual(map.count, 3)
|
||||
XCTAssertEqual(map.keys, ["one", "two", "three"], "Insertion order should be preserved")
|
||||
|
||||
var iteratedKeys = [String]()
|
||||
for (key, _) in map {
|
||||
iteratedKeys.append(key)
|
||||
}
|
||||
XCTAssertEqual(iteratedKeys, ["one", "two", "three"], "Iterator should follow insertion order")
|
||||
}
|
||||
|
||||
// Test that updating a key does not change its order.
|
||||
func testUpdateDoesNotChangeOrder() {
|
||||
let map = LinkedHashMap<String, Int>()
|
||||
map.put(key: "a", value: 1)
|
||||
map.put(key: "b", value: 2)
|
||||
map.put(key: "c", value: 3)
|
||||
// Update key "b"
|
||||
map.put(key: "b", value: 20)
|
||||
XCTAssertEqual(map.get(key: "b"), 20)
|
||||
|
||||
XCTAssertEqual(map.keys, ["a", "b", "c"], "Order should not change on update")
|
||||
}
|
||||
|
||||
// Test removal functionality and behavior when removing a non-existent key.
|
||||
func testRemoval() {
|
||||
let map = LinkedHashMap<Int, String>()
|
||||
map.put(key: 1, value: "one")
|
||||
map.put(key: 2, value: "two")
|
||||
map.put(key: 3, value: "three")
|
||||
|
||||
let removed = map.remove(key: 2)
|
||||
XCTAssertEqual(removed, "two")
|
||||
XCTAssertEqual(map.count, 2)
|
||||
XCTAssertEqual(map.keys, [1, 3])
|
||||
|
||||
// Removing a key that doesn't exist should return nil.
|
||||
let removedNil = map.remove(key: 4)
|
||||
XCTAssertNil(removedNil)
|
||||
}
|
||||
|
||||
// Test clearing the map.
|
||||
func testClear() {
|
||||
let map = LinkedHashMap<String, Int>()
|
||||
map.put(key: "x", value: 100)
|
||||
map.put(key: "y", value: 200)
|
||||
XCTAssertEqual(map.count, 2)
|
||||
|
||||
map.clear()
|
||||
XCTAssertEqual(map.count, 0)
|
||||
XCTAssertTrue(map.isEmpty)
|
||||
XCTAssertEqual(map.keys, [])
|
||||
XCTAssertEqual(map.values, [])
|
||||
}
|
||||
|
||||
// Test subscript access for getting, updating, and removal.
|
||||
func testSubscript() {
|
||||
let map = LinkedHashMap<String, Int>()
|
||||
map["alpha"] = 10
|
||||
XCTAssertEqual(map["alpha"], 10)
|
||||
|
||||
map["alpha"] = 20
|
||||
XCTAssertEqual(map["alpha"], 20)
|
||||
|
||||
// Setting a key to nil should remove the mapping.
|
||||
map["alpha"] = nil
|
||||
XCTAssertNil(map["alpha"])
|
||||
}
|
||||
|
||||
// Test containsKey and containsValue.
|
||||
func testContains() {
|
||||
let map = LinkedHashMap<String, Int>()
|
||||
map.put(key: "key1", value: 1)
|
||||
map.put(key: "key2", value: 2)
|
||||
|
||||
XCTAssertTrue(map.containsKey("key1"))
|
||||
XCTAssertFalse(map.containsKey("key3"))
|
||||
|
||||
XCTAssertTrue(map.containsValue(1))
|
||||
XCTAssertFalse(map.containsValue(99))
|
||||
}
|
||||
|
||||
// Test initialization from a dictionary.
|
||||
func testInitializationFromDictionary() {
|
||||
// Note: Swift dictionaries preserve insertion order for literals.
|
||||
let dictionary: [String: Int] = ["a": 1, "b": 2, "c": 3]
|
||||
let map = LinkedHashMap(dictionary)
|
||||
XCTAssertEqual(map.count, 3)
|
||||
// Order may differ since Dictionary order is not strictly defined – here we verify membership.
|
||||
XCTAssertEqual(Set(map.keys), Set(["a", "b", "c"]))
|
||||
}
|
||||
|
||||
// Revised test that iterates over the map and compares key-value pairs element by element.
|
||||
func testIteration() {
|
||||
let map = LinkedHashMap<Int, String>()
|
||||
let pairs = [(1, "one"), (2, "two"), (3, "three")]
|
||||
for (key, value) in pairs {
|
||||
map.put(key: key, value: value)
|
||||
}
|
||||
|
||||
var iteratedPairs = [(Int, String)]()
|
||||
for (key, value) in map {
|
||||
iteratedPairs.append((key, value))
|
||||
}
|
||||
|
||||
XCTAssertEqual(iteratedPairs.count, pairs.count, "Iterated count should match inserted count")
|
||||
for (iter, expected) in zip(iteratedPairs, pairs) {
|
||||
XCTAssertEqual(iter.0, expected.0, "Keys should match in order")
|
||||
XCTAssertEqual(iter.1, expected.1, "Values should match in order")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the values stored in the map are deallocated when the map is deallocated.
|
||||
func testMemoryLeak() {
|
||||
weak var weakMap: LinkedHashMap<Int, LeakTester>?
|
||||
var deinitCalled = false
|
||||
|
||||
do {
|
||||
let map = LinkedHashMap<Int, LeakTester>()
|
||||
let tester = LeakTester(id: 1) { deinitCalled = true }
|
||||
map.put(key: 1, value: tester)
|
||||
weakMap = map
|
||||
XCTAssertNotNil(map.get(key: 1))
|
||||
}
|
||||
// At this point the map (and its stored objects) should be deallocated.
|
||||
XCTAssertNil(weakMap, "LinkedHashMap should be deallocated when out of scope")
|
||||
XCTAssertTrue(deinitCalled, "LeakTester should be deallocated, indicating no memory leak")
|
||||
}
|
||||
|
||||
// Test that removal from the map correctly frees stored objects.
|
||||
func testMemoryLeakOnRemoval() {
|
||||
var deinitCalledForTester1 = false
|
||||
var deinitCalledForTester2 = false
|
||||
|
||||
let map = LinkedHashMap<Int, LeakTester>()
|
||||
autoreleasepool {
|
||||
let tester1 = LeakTester(id: 1) { deinitCalledForTester1 = true }
|
||||
let tester2 = LeakTester(id: 2) { deinitCalledForTester2 = true }
|
||||
map.put(key: 1, value: tester1)
|
||||
map.put(key: 2, value: tester2)
|
||||
|
||||
XCTAssertNotNil(map.get(key: 1))
|
||||
XCTAssertNotNil(map.get(key: 2))
|
||||
|
||||
// Remove tester1; it should be deallocated if no retain cycle exists.
|
||||
_ = map.remove(key: 1)
|
||||
}
|
||||
// tester1 should be deallocated immediately after removal.
|
||||
XCTAssertTrue(deinitCalledForTester1, "Tester1 should be deallocated after removal")
|
||||
// tester2 is still in the map.
|
||||
XCTAssertNotNil(map.get(key: 2))
|
||||
|
||||
// Clear the map and tester2 should be deallocated.
|
||||
map.clear()
|
||||
XCTAssertTrue(deinitCalledForTester2, "Tester2 should be deallocated after clearing the map")
|
||||
}
|
||||
|
||||
func testDefaultSubscriptExtension() {
|
||||
// Create an instance of LinkedHashMap with String keys and Bool values.
|
||||
let map = LinkedHashMap<String, Bool>()
|
||||
|
||||
// Verify that accessing a non-existent key returns the default value (false).
|
||||
XCTAssertEqual(map["testKey", default: false], false)
|
||||
|
||||
// Use the default subscript setter to assign 'true' for the key.
|
||||
map["testKey", default: false] = true
|
||||
XCTAssertEqual(map["testKey", default: false], true)
|
||||
|
||||
// Simulate in-place toggle: read the value, toggle it, then write it back.
|
||||
var current = map["testKey", default: false]
|
||||
current.toggle() // now false
|
||||
map["testKey", default: false] = current
|
||||
XCTAssertEqual(map["testKey", default: false], false)
|
||||
}
|
||||
}
|
||||
154
SideStore/Tests/UnitTests/datastructures/TreeMapTests.swift
Normal file
154
SideStore/Tests/UnitTests/datastructures/TreeMapTests.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// TreeMapTests.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Magesh K on 21/02/25.
|
||||
// Copyright © 2025 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import XCTest
|
||||
|
||||
class TreeMapTests: XCTestCase {
|
||||
|
||||
func testInsertionAndRetrieval() {
|
||||
let map = TreeMap<Int, String>()
|
||||
XCTAssertNil(map[10])
|
||||
map[10] = "ten"
|
||||
XCTAssertEqual(map[10], "ten")
|
||||
|
||||
map[5] = "five"
|
||||
map[15] = "fifteen"
|
||||
XCTAssertEqual(map.count, 3)
|
||||
XCTAssertEqual(map[5], "five")
|
||||
XCTAssertEqual(map[15], "fifteen")
|
||||
}
|
||||
|
||||
func testUpdateValue() {
|
||||
let map = TreeMap<Int, String>()
|
||||
map[10] = "ten"
|
||||
let oldValue = map.insert(key: 10, value: "TEN")
|
||||
XCTAssertEqual(oldValue, "ten")
|
||||
XCTAssertEqual(map[10], "TEN")
|
||||
XCTAssertEqual(map.count, 1)
|
||||
}
|
||||
|
||||
func testDeletion() {
|
||||
let map = TreeMap<Int, String>()
|
||||
// Setup: Inserting three nodes.
|
||||
map[20] = "twenty"
|
||||
map[10] = "ten"
|
||||
map[30] = "thirty"
|
||||
|
||||
// Remove a leaf node.
|
||||
let removedLeaf = map.remove(key: 10)
|
||||
XCTAssertEqual(removedLeaf, "ten")
|
||||
XCTAssertNil(map[10])
|
||||
XCTAssertEqual(map.count, 2)
|
||||
|
||||
// Setup additional nodes to create a one-child scenario.
|
||||
map[25] = "twenty-five"
|
||||
map[27] = "twenty-seven" // Right child for 25.
|
||||
// Remove a node with one child.
|
||||
let removedOneChild = map.remove(key: 25)
|
||||
XCTAssertEqual(removedOneChild, "twenty-five")
|
||||
XCTAssertNil(map[25])
|
||||
XCTAssertEqual(map.count, 3)
|
||||
|
||||
// Setup for a node with two children.
|
||||
map[40] = "forty"
|
||||
map[35] = "thirty-five"
|
||||
map[45] = "forty-five"
|
||||
// Remove a node with two children.
|
||||
let removedTwoChildren = map.remove(key: 40)
|
||||
XCTAssertEqual(removedTwoChildren, "forty")
|
||||
XCTAssertNil(map[40])
|
||||
XCTAssertEqual(map.count, 5)
|
||||
}
|
||||
|
||||
func testDeletionOfRoot() {
|
||||
let map = TreeMap<Int, String>()
|
||||
map[50] = "fifty"
|
||||
map[30] = "thirty"
|
||||
map[70] = "seventy"
|
||||
|
||||
// Delete the root node.
|
||||
let removedRoot = map.remove(key: 50)
|
||||
XCTAssertEqual(removedRoot, "fifty")
|
||||
XCTAssertNil(map[50])
|
||||
// After deletion, remaining keys should be in sorted order.
|
||||
XCTAssertEqual(map.keys, [30, 70])
|
||||
}
|
||||
|
||||
func testSortedIteration() {
|
||||
let map = TreeMap<Int, String>()
|
||||
let keys = [20, 10, 30, 5, 15, 25, 35]
|
||||
for key in keys {
|
||||
map[key] = "\(key)"
|
||||
}
|
||||
let sortedKeys = map.keys
|
||||
XCTAssertEqual(sortedKeys, keys.sorted())
|
||||
|
||||
// Verify in-order traversal.
|
||||
var previous: Int? = nil
|
||||
for (key, value) in map {
|
||||
if let prev = previous {
|
||||
XCTAssertLessThanOrEqual(prev, key)
|
||||
}
|
||||
previous = key
|
||||
XCTAssertEqual(value, "\(key)")
|
||||
}
|
||||
}
|
||||
|
||||
func testRemoveAll() {
|
||||
let map = TreeMap<Int, String>()
|
||||
for i in 0..<100 {
|
||||
map[i] = "\(i)"
|
||||
}
|
||||
XCTAssertEqual(map.count, 100)
|
||||
map.removeAll()
|
||||
XCTAssertEqual(map.count, 0)
|
||||
XCTAssertTrue(map.isEmpty)
|
||||
}
|
||||
|
||||
func testBalancing() {
|
||||
let map = TreeMap<Int, Int>()
|
||||
// Insert elements in ascending order to challenge the balancing.
|
||||
for i in 1...1000 {
|
||||
map[i] = i
|
||||
}
|
||||
// Verify in-order traversal produces sorted order.
|
||||
var expected = 1
|
||||
for (key, value) in map {
|
||||
XCTAssertEqual(key, expected)
|
||||
XCTAssertEqual(value, expected)
|
||||
expected += 1
|
||||
}
|
||||
XCTAssertEqual(expected - 1, 1000)
|
||||
|
||||
// Remove odd keys to force rebalancing.
|
||||
for i in stride(from: 1, through: 1000, by: 2) {
|
||||
_ = map.remove(key: i)
|
||||
}
|
||||
let expectedEvenKeys = (1...1000).filter { $0 % 2 == 0 }
|
||||
XCTAssertEqual(map.keys, expectedEvenKeys)
|
||||
}
|
||||
|
||||
func testNonExistentDeletion() {
|
||||
let map = TreeMap<Int, String>()
|
||||
map[10] = "ten"
|
||||
let removed = map.remove(key: 20)
|
||||
XCTAssertNil(removed)
|
||||
XCTAssertEqual(map.count, 1)
|
||||
}
|
||||
|
||||
func testDuplicateInsertion() {
|
||||
let map = TreeMap<String, String>()
|
||||
map["a"] = "first"
|
||||
XCTAssertEqual(map["a"], "first")
|
||||
let oldValue = map.insert(key: "a", value: "second")
|
||||
XCTAssertEqual(oldValue, "first")
|
||||
XCTAssertEqual(map["a"], "second")
|
||||
XCTAssertEqual(map.count, 1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user