summaryrefslogtreecommitdiff
path: root/SoraTests/ViewDerivedDataTests.swift
blob: da5ad0d816a78eb3c62b5ed1f3bb98c4de7410ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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."
    )
  }

  func testFavoritesViewDerivedCollectionsAreReferencedOncePerRenderPass() throws {
    let source = try loadSource(at: "Sora/Views/FavoritesView.swift")
    let normalizedSource = strippingCommentsAndStrings(from: source)

    let filteredFavoritesUsages = referenceCount(
      for: "filteredFavorites",
      in: normalizedSource
    )
    let sortedFilteredFavoritesUsages = referenceCount(
      for: "sortedFilteredFavorites",
      in: normalizedSource
    )
    let inlinePostMappingCount = tokenCount(
      matching: #"\bsortedFilteredFavorites\s*\.\s*map\s*\{"#,
      in: normalizedSource
    )
    let columnsSplitterUsages = invocationCount(
      forFunction: "getColumnsData",
      in: normalizedSource
    )

    // swiftlint:disable:next prefer_nimble
    XCTAssertLessThanOrEqual(
      filteredFavoritesUsages,
      1,
      "filteredFavorites should be consumed once per dependency change."
    )
    // swiftlint:disable:next prefer_nimble
    XCTAssertLessThanOrEqual(
      sortedFilteredFavoritesUsages,
      1,
      "sortedFilteredFavorites should be consumed once per dependency change."
    )
    // swiftlint:disable:next prefer_nimble
    XCTAssertEqual(
      inlinePostMappingCount,
      0,
      "Favorites grid should not remap sorted favorites to posts inline on each cell render."
    )
    // swiftlint:disable:next prefer_nimble
    XCTAssertLessThanOrEqual(
      columnsSplitterUsages,
      1,
      "Favorites grid column distribution should be derived once per dependency change."
    )
  }

  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)
  }
}