summaryrefslogtreecommitdiff
path: root/SoraTests
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-18 11:41:58 -0800
committerFuwn <[email protected]>2026-02-18 11:42:11 -0800
commitdd690e6be3bba1f0d3aa6e853e50e23cf44f75ec (patch)
tree146ec3964c02579f63465c88199fcc8270d0c4e1 /SoraTests
parentFix SwiftLint violations (diff)
downloadsora-testing-dd690e6be3bba1f0d3aa6e853e50e23cf44f75ec.tar.xz
sora-testing-dd690e6be3bba1f0d3aa6e853e50e23cf44f75ec.zip
test: add baseline test harness and performance baseline report
Diffstat (limited to 'SoraTests')
-rw-r--r--SoraTests/SettingsManagerSyncTests.swift237
-rw-r--r--SoraTests/ViewDerivedDataTests.swift192
2 files changed, 429 insertions, 0 deletions
diff --git a/SoraTests/SettingsManagerSyncTests.swift b/SoraTests/SettingsManagerSyncTests.swift
new file mode 100644
index 0000000..27f05f5
--- /dev/null
+++ b/SoraTests/SettingsManagerSyncTests.swift
@@ -0,0 +1,237 @@
+import Foundation
+import XCTest
+
+final class SettingsManagerSyncTests: XCTestCase {
+ func testBatchedSyncPathGuardsUnchangedPayloadWrites() throws {
+ let source = try loadSource(at: "Sora/Data/Settings/SettingsManager.swift")
+ let triggerSyncSection = try extractFunction(
+ named: "private func triggerSyncIfNeeded(for key: SettingsSyncKey)",
+ from: source
+ )
+ let normalizedSection = strippingCommentsAndStrings(from: triggerSyncSection)
+
+ let cloudWriteCount = tokenCount(
+ matching: #"\bNSUbiquitousKeyValueStore\s*\.\s*default\s*\.\s*set\s*\(\s*encoded\s*,\s*forKey\s*:"#,
+ in: normalizedSection
+ )
+ let unchangedGuardCount = tokenCount(
+ matching: #"\!\=\s*encoded\b"#,
+ in: normalizedSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(cloudWriteCount, 0, "Expected batched sync path to contain cloud writes.")
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThanOrEqual(
+ unchangedGuardCount,
+ cloudWriteCount,
+ "Every batched iCloud write should be guarded to avoid unchanged payload rewrites."
+ )
+ }
+
+ private func loadSource(at relativePath: String) throws -> String {
+ let currentFile = URL(fileURLWithPath: #filePath)
+ let repositoryRoot = currentFile.deletingLastPathComponent().deletingLastPathComponent()
+ let fileURL = repositoryRoot.appendingPathComponent(relativePath)
+
+ return try String(contentsOf: fileURL, encoding: .utf8)
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ private func extractFunction(named signature: String, from source: String) throws -> String {
+ guard let signatureRange = source.range(of: signature) else {
+ throw NSError(domain: "SettingsManagerSyncTests", code: 1)
+ }
+ guard let openingBrace = source[signatureRange.upperBound...].firstIndex(of: "{") else {
+ throw NSError(domain: "SettingsManagerSyncTests", code: 2)
+ }
+
+ let characters = Array(source)
+ let startOffset = source.distance(from: source.startIndex, to: signatureRange.lowerBound)
+ var currentOffset = source.distance(from: source.startIndex, to: openingBrace)
+
+ var braceDepth = 0
+ var inLineComment = false
+ var blockCommentDepth = 0
+ var inString = false
+ var isEscaped = false
+
+ while currentOffset < characters.count {
+ let current = characters[currentOffset]
+ let next: Character? = currentOffset + 1 < characters.count ? characters[currentOffset + 1] : nil
+
+ if inLineComment {
+ if current == "\n" {
+ inLineComment = false
+ }
+ currentOffset += 1
+ continue
+ }
+
+ if blockCommentDepth > 0 {
+ if current == "/", next == "*" {
+ blockCommentDepth += 1
+ currentOffset += 2
+ continue
+ }
+
+ if current == "*", next == "/" {
+ blockCommentDepth -= 1
+ currentOffset += 2
+ continue
+ }
+
+ currentOffset += 1
+ continue
+ }
+
+ if inString {
+ if isEscaped {
+ isEscaped = false
+ } else if current == "\\" {
+ isEscaped = true
+ } else if current == "\"" {
+ inString = false
+ }
+
+ currentOffset += 1
+ continue
+ }
+
+ if current == "/", next == "/" {
+ inLineComment = true
+ currentOffset += 2
+ continue
+ }
+
+ if current == "/", next == "*" {
+ blockCommentDepth = 1
+ currentOffset += 2
+ continue
+ }
+
+ if current == "\"" {
+ inString = true
+ currentOffset += 1
+ continue
+ }
+
+ if current == "{" {
+ braceDepth += 1
+ } else if current == "}" {
+ braceDepth -= 1
+
+ if braceDepth == 0 {
+ let endIndex = source.index(source.startIndex, offsetBy: currentOffset + 1)
+ let startIndex = source.index(source.startIndex, offsetBy: startOffset)
+
+ return String(source[startIndex..<endIndex])
+ }
+ }
+
+ currentOffset += 1
+ }
+
+ throw NSError(domain: "SettingsManagerSyncTests", code: 3)
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ private func strippingCommentsAndStrings(from source: String) -> String {
+ let characters = Array(source)
+ var result: [Character] = []
+ result.reserveCapacity(characters.count)
+
+ var currentOffset = 0
+ var inLineComment = false
+ var blockCommentDepth = 0
+ var inString = false
+ var isEscaped = false
+
+ while currentOffset < characters.count {
+ let current = characters[currentOffset]
+ let next: Character? = currentOffset + 1 < characters.count ? characters[currentOffset + 1] : nil
+
+ if inLineComment {
+ if current == "\n" {
+ inLineComment = false
+ result.append("\n")
+ } else {
+ result.append(" ")
+ }
+ currentOffset += 1
+ continue
+ }
+
+ if blockCommentDepth > 0 {
+ if current == "/", next == "*" {
+ blockCommentDepth += 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "*", next == "/" {
+ blockCommentDepth -= 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if inString {
+ if isEscaped {
+ isEscaped = false
+ } else if current == "\\" {
+ isEscaped = true
+ } else if current == "\"" {
+ inString = false
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if current == "/", next == "/" {
+ inLineComment = true
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "/", next == "*" {
+ blockCommentDepth = 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "\"" {
+ inString = true
+ result.append(" ")
+ currentOffset += 1
+ continue
+ }
+
+ result.append(current)
+ currentOffset += 1
+ }
+
+ return String(result)
+ }
+
+ private func tokenCount(matching pattern: String, in source: String) -> Int {
+ guard let regex = try? NSRegularExpression(pattern: pattern) else { return 0 }
+ let range = NSRange(source.startIndex..<source.endIndex, in: source)
+
+ return regex.numberOfMatches(in: source, range: range)
+ }
+}
diff --git a/SoraTests/ViewDerivedDataTests.swift b/SoraTests/ViewDerivedDataTests.swift
new file mode 100644
index 0000000..6737265
--- /dev/null
+++ b/SoraTests/ViewDerivedDataTests.swift
@@ -0,0 +1,192 @@
+import Foundation
+import XCTest
+
+final class ViewDerivedDataTests: XCTestCase {
+ func testGenericListViewDerivedCollectionsAreReferencedOncePerRenderPass() throws {
+ let source = try loadSource(at: "Sora/Views/Generic/GenericListView.swift")
+ let normalizedSource = strippingCommentsAndStrings(from: source)
+
+ let filteredItemsUsages = referenceCount(
+ for: "filteredItems",
+ in: normalizedSource
+ )
+ let sortedFilteredItemsUsages = referenceCount(
+ for: "sortedFilteredItems",
+ in: normalizedSource
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertLessThanOrEqual(
+ filteredItemsUsages,
+ 1,
+ "filteredItems should be consumed once per dependency change."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertLessThanOrEqual(
+ sortedFilteredItemsUsages,
+ 1,
+ "sortedFilteredItems should be consumed once per dependency change."
+ )
+ }
+
+ func testPostGridViewDerivedCollectionsAreReferencedOncePerRenderPass() throws {
+ let source = try loadSource(at: "Sora/Views/Post/Grid/PostGridView.swift")
+ let normalizedSource = strippingCommentsAndStrings(from: source)
+
+ let activePostsUsages = referenceCount(
+ for: "activePosts",
+ in: normalizedSource
+ )
+ let getColumnsDataUsages = invocationCount(
+ forFunction: "getColumnsData",
+ in: normalizedSource
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertLessThanOrEqual(
+ activePostsUsages,
+ 1,
+ "activePosts-derived data should be consumed once per dependency change."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertLessThanOrEqual(
+ getColumnsDataUsages,
+ 1,
+ "getColumnsData should be invoked once per dependency change."
+ )
+ }
+
+ private func loadSource(at relativePath: String) throws -> String {
+ let currentFile = URL(fileURLWithPath: #filePath)
+ let repositoryRoot = currentFile.deletingLastPathComponent().deletingLastPathComponent()
+ let fileURL = repositoryRoot.appendingPathComponent(relativePath)
+
+ return try String(contentsOf: fileURL, encoding: .utf8)
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ private func strippingCommentsAndStrings(from source: String) -> String {
+ let characters = Array(source)
+ var result: [Character] = []
+ result.reserveCapacity(characters.count)
+
+ var currentOffset = 0
+ var inLineComment = false
+ var blockCommentDepth = 0
+ var inString = false
+ var isEscaped = false
+
+ while currentOffset < characters.count {
+ let current = characters[currentOffset]
+ let next: Character? = currentOffset + 1 < characters.count ? characters[currentOffset + 1] : nil
+
+ if inLineComment {
+ if current == "\n" {
+ inLineComment = false
+ result.append("\n")
+ } else {
+ result.append(" ")
+ }
+ currentOffset += 1
+ continue
+ }
+
+ if blockCommentDepth > 0 {
+ if current == "/", next == "*" {
+ blockCommentDepth += 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "*", next == "/" {
+ blockCommentDepth -= 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if inString {
+ if isEscaped {
+ isEscaped = false
+ } else if current == "\\" {
+ isEscaped = true
+ } else if current == "\"" {
+ inString = false
+ }
+
+ result.append(current == "\n" ? "\n" : " ")
+ currentOffset += 1
+ continue
+ }
+
+ if current == "/", next == "/" {
+ inLineComment = true
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "/", next == "*" {
+ blockCommentDepth = 1
+ result.append(" ")
+ result.append(" ")
+ currentOffset += 2
+ continue
+ }
+
+ if current == "\"" {
+ inString = true
+ result.append(" ")
+ currentOffset += 1
+ continue
+ }
+
+ result.append(current)
+ currentOffset += 1
+ }
+
+ return String(result)
+ }
+
+ private func referenceCount(for symbol: String, in source: String) -> Int {
+ let totalMatches = tokenCount(
+ matching: #"\b\#(symbol)\b"#,
+ in: source
+ )
+ let declarationMatches = tokenCount(
+ matching: #"\b(?:var|let)\s+\#(symbol)\b"#,
+ in: source
+ )
+
+ return max(0, totalMatches - declarationMatches)
+ }
+
+ private func invocationCount(forFunction name: String, in source: String) -> Int {
+ let totalMatches = tokenCount(
+ matching: #"\b\#(name)\s*\("#,
+ in: source
+ )
+ let declarationMatches = tokenCount(
+ matching: #"\bfunc\s+\#(name)\s*\("#,
+ in: source
+ )
+
+ return max(0, totalMatches - declarationMatches)
+ }
+
+ private func tokenCount(matching pattern: String, in source: String) -> Int {
+ guard let regex = try? NSRegularExpression(pattern: pattern) else { return 0 }
+ let range = NSRange(source.startIndex..<source.endIndex, in: source)
+
+ return regex.numberOfMatches(in: source, range: range)
+ }
+}