|
11 | 11 | import Foundation
|
12 | 12 | import XCTest
|
13 | 13 | @testable import SwiftDocC
|
| 14 | +import SwiftDocCTestUtilities |
14 | 15 |
|
15 | 16 | class DeclarationsRenderSectionTests: XCTestCase {
|
16 | 17 | func testDecodingTokens() throws {
|
@@ -76,7 +77,7 @@ class DeclarationsRenderSectionTests: XCTestCase {
|
76 | 77 | )
|
77 | 78 | }
|
78 | 79 | }
|
79 |
| - |
| 80 | + |
80 | 81 | func testDoNotEmitOtherDeclarationsIfEmpty() throws {
|
81 | 82 |
|
82 | 83 | let encoder = RenderJSONEncoder.makeEncoder(prettyPrint: true)
|
@@ -151,4 +152,230 @@ class DeclarationsRenderSectionTests: XCTestCase {
|
151 | 152 | XCTAssertEqual(declarationsSection.declarations.count, 2)
|
152 | 153 | XCTAssert(declarationsSection.declarations.allSatisfy({ $0.platforms == [.iOS, .macOS] }))
|
153 | 154 | }
|
| 155 | + |
| 156 | + func testHighlightDiff() throws { |
| 157 | + enableFeatureFlag(\.isExperimentalOverloadedSymbolPresentationEnabled) |
| 158 | + |
| 159 | + let symbolGraphFile = Bundle.module.url( |
| 160 | + forResource: "FancyOverloads", |
| 161 | + withExtension: "symbols.json", |
| 162 | + subdirectory: "Test Resources" |
| 163 | + )! |
| 164 | + |
| 165 | + let tempURL = try createTempFolder(content: [ |
| 166 | + Folder(name: "unit-test.docc", content: [ |
| 167 | + InfoPlist(displayName: "FancyOverloads", identifier: "com.test.example"), |
| 168 | + CopyOfFile(original: symbolGraphFile), |
| 169 | + ]) |
| 170 | + ]) |
| 171 | + |
| 172 | + let (_, bundle, context) = try loadBundle(from: tempURL) |
| 173 | + |
| 174 | + // Make sure that type decorators like arrays, dictionaries, and optionals are correctly highlighted. |
| 175 | + do { |
| 176 | + // func overload1(param: Int) {} // <- overload group |
| 177 | + // func overload1(param: Int?) {} |
| 178 | + // func overload1(param: [Int]) {} |
| 179 | + // func overload1(param: [Int]?) {} |
| 180 | + // func overload1(param: Set<Int>) {} |
| 181 | + // func overload1(param: [Int: Int]) {} |
| 182 | + let reference = ResolvedTopicReference( |
| 183 | + bundleIdentifier: bundle.identifier, |
| 184 | + path: "/documentation/FancyOverloads/overload1(param:)-8nk5z", |
| 185 | + sourceLanguage: .swift |
| 186 | + ) |
| 187 | + let symbol = try XCTUnwrap(context.entity(with: reference).semantic as? Symbol) |
| 188 | + var translator = RenderNodeTranslator( |
| 189 | + context: context, |
| 190 | + bundle: bundle, |
| 191 | + identifier: reference, |
| 192 | + source: nil |
| 193 | + ) |
| 194 | + let renderNode = try XCTUnwrap(translator.visitSymbol(symbol) as? RenderNode) |
| 195 | + let declarationsSection = try XCTUnwrap(renderNode.primaryContentSections.compactMap({ $0 as? DeclarationsRenderSection }).first) |
| 196 | + XCTAssertEqual(declarationsSection.declarations.count, 1) |
| 197 | + let declarations = try XCTUnwrap(declarationsSection.declarations.first) |
| 198 | + |
| 199 | + XCTAssertEqual( |
| 200 | + declarationAndHighlights(for: declarations.tokens), |
| 201 | + [ |
| 202 | + "func overload1(param: Int)", |
| 203 | + " ", |
| 204 | + ] |
| 205 | + ) |
| 206 | + |
| 207 | + XCTAssertEqual( |
| 208 | + declarations.otherDeclarations?.declarations.flatMap({ declarationAndHighlights(for: $0.tokens) }), |
| 209 | + [ |
| 210 | + "func overload1(param: Int?)", |
| 211 | + " ~ ", |
| 212 | + |
| 213 | + "func overload1(param: Set<Int>)", |
| 214 | + " ~~~~ ~ ", |
| 215 | + |
| 216 | + "func overload1(param: [Int : Int])", |
| 217 | + " ~ ~~~~~~ ", |
| 218 | + |
| 219 | + "func overload1(param: [Int])", |
| 220 | + " ~ ~ ", |
| 221 | + |
| 222 | + "func overload1(param: [Int]?)", |
| 223 | + " ~ ~~ ", |
| 224 | + ] |
| 225 | + ) |
| 226 | + } |
| 227 | + |
| 228 | + // Verify the behavior of the highlighter in the face of tuples and closures, which can |
| 229 | + // confuse the differencing code with excess parentheses and commas. |
| 230 | + do { |
| 231 | + // func overload2(p1: Int, p2: Int) {} |
| 232 | + // func overload2(p1: (Int, Int), p2: Int) {} |
| 233 | + // func overload2(p1: Int, p2: (Int, Int)) {} |
| 234 | + // func overload2(p1: (Int) -> (), p2: Int) {} |
| 235 | + // func overload2(p1: (Int) -> Int, p2: Int) {} |
| 236 | + // func overload2(p1: (Int) -> Int?, p2: Int) {} |
| 237 | + // func overload2(p1: ((Int) -> Int)?, p2: Int) {} // <- overload group |
| 238 | + let reference = ResolvedTopicReference( |
| 239 | + bundleIdentifier: bundle.identifier, |
| 240 | + path: "/documentation/FancyOverloads/overload2(p1:p2:)-4p1sq", |
| 241 | + sourceLanguage: .swift |
| 242 | + ) |
| 243 | + let symbol = try XCTUnwrap(context.entity(with: reference).semantic as? Symbol) |
| 244 | + var translator = RenderNodeTranslator( |
| 245 | + context: context, |
| 246 | + bundle: bundle, |
| 247 | + identifier: reference, |
| 248 | + source: nil |
| 249 | + ) |
| 250 | + let renderNode = try XCTUnwrap(translator.visitSymbol(symbol) as? RenderNode) |
| 251 | + let declarationsSection = try XCTUnwrap(renderNode.primaryContentSections.compactMap({ $0 as? DeclarationsRenderSection }).first) |
| 252 | + XCTAssertEqual(declarationsSection.declarations.count, 1) |
| 253 | + let declarations = try XCTUnwrap(declarationsSection.declarations.first) |
| 254 | + |
| 255 | + XCTAssertEqual( |
| 256 | + declarationAndHighlights(for: declarations.tokens), |
| 257 | + [ |
| 258 | + "func overload2(p1: ((Int) -> Int)?, p2: Int)", |
| 259 | + " ~~ ~~~~~~~~~~ " |
| 260 | + ] |
| 261 | + ) |
| 262 | + |
| 263 | + XCTAssertEqual( |
| 264 | + declarations.otherDeclarations?.declarations.flatMap({ declarationAndHighlights(for: $0.tokens) }), |
| 265 | + [ |
| 266 | + "func overload2(p1: (Int) -> (), p2: Int)", |
| 267 | + " ~ ~~~~~~~ ", |
| 268 | + |
| 269 | + "func overload2(p1: (Int) -> Int, p2: Int)", |
| 270 | + " ~ ~~~~~~~~ ", |
| 271 | + |
| 272 | + "func overload2(p1: (Int) -> Int?, p2: Int)", |
| 273 | + " ~ ~~~~~~~~~ ", |
| 274 | + |
| 275 | + // FIXME: adjust the token processing so that the comma inside the tuple isn't treated as common? |
| 276 | + // (it breaks the declaration pretty-printer in Swift-DocC-Render and causes it to skip pretty-printing) |
| 277 | + "func overload2(p1: (Int, Int), p2: Int)", |
| 278 | + " ~ ~~~~~ ", |
| 279 | + |
| 280 | + // FIXME: adjust the token processing so that the common parenthesis is always the final one |
| 281 | + "func overload2(p1: Int, p2: (Int, Int))", |
| 282 | + " ~ ~~~~~ ~", |
| 283 | + |
| 284 | + "func overload2(p1: Int, p2: Int)", |
| 285 | + " ", |
| 286 | + ] |
| 287 | + ) |
| 288 | + } |
| 289 | + |
| 290 | + // Verify that the presence of type parameters doesn't cause the opening parenthesis of an |
| 291 | + // argument list to also be highlighted, since it is combined into the same token as the |
| 292 | + // closing angle bracket in the symbol graph. Also ensure that the leading space of the |
| 293 | + // rendered where clause is not highlighted. |
| 294 | + do { |
| 295 | + // func overload3(_ p: [Int: Int]) {} // <- overload group |
| 296 | + // func overload3<T: Hashable>(_ p: [T: T]) {} |
| 297 | + // func overload3<K: Hashable, V>(_ p: [K: V]) {} |
| 298 | + let reference = ResolvedTopicReference( |
| 299 | + bundleIdentifier: bundle.identifier, |
| 300 | + path: "/documentation/FancyOverloads/overload3(_:)-xql2", |
| 301 | + sourceLanguage: .swift |
| 302 | + ) |
| 303 | + let symbol = try XCTUnwrap(context.entity(with: reference).semantic as? Symbol) |
| 304 | + var translator = RenderNodeTranslator( |
| 305 | + context: context, |
| 306 | + bundle: bundle, |
| 307 | + identifier: reference, |
| 308 | + source: nil |
| 309 | + ) |
| 310 | + let renderNode = try XCTUnwrap(translator.visitSymbol(symbol) as? RenderNode) |
| 311 | + let declarationsSection = try XCTUnwrap(renderNode.primaryContentSections.compactMap({ $0 as? DeclarationsRenderSection }).first) |
| 312 | + XCTAssertEqual(declarationsSection.declarations.count, 1) |
| 313 | + let declarations = try XCTUnwrap(declarationsSection.declarations.first) |
| 314 | + |
| 315 | + XCTAssertEqual( |
| 316 | + declarationAndHighlights(for: declarations.tokens), |
| 317 | + [ |
| 318 | + "func overload3(_ p: [Int : Int])", |
| 319 | + " ~~~ ~~~ ", |
| 320 | + ] |
| 321 | + ) |
| 322 | + |
| 323 | + XCTAssertEqual( |
| 324 | + declarations.otherDeclarations?.declarations.flatMap({ declarationAndHighlights(for: $0.tokens) }), |
| 325 | + [ |
| 326 | + "func overload3<K, V>(_ p: [K : V]) where K : Hashable", |
| 327 | + " ~~~~~~ ~ ~ ~~~~~~~~~~~~~~~~~~", |
| 328 | + |
| 329 | + "func overload3<T>(_ p: [T : T]) where T : Hashable", |
| 330 | + " ~~~ ~ ~ ~~~~~~~~~~~~~~~~~~", |
| 331 | + ] |
| 332 | + ) |
| 333 | + } |
| 334 | + } |
| 335 | + |
| 336 | + func testDontHighlightWhenOverloadsAreDisabled() throws { |
| 337 | + let symbolGraphFile = Bundle.module.url( |
| 338 | + forResource: "FancyOverloads", |
| 339 | + withExtension: "symbols.json", |
| 340 | + subdirectory: "Test Resources" |
| 341 | + )! |
| 342 | + |
| 343 | + let tempURL = try createTempFolder(content: [ |
| 344 | + Folder(name: "unit-test.docc", content: [ |
| 345 | + InfoPlist(displayName: "FancyOverloads", identifier: "com.test.example"), |
| 346 | + CopyOfFile(original: symbolGraphFile), |
| 347 | + ]) |
| 348 | + ]) |
| 349 | + |
| 350 | + let (_, bundle, context) = try loadBundle(from: tempURL) |
| 351 | + |
| 352 | + for hash in ["7eht8", "8p1lo", "858ja"] { |
| 353 | + let reference = ResolvedTopicReference( |
| 354 | + bundleIdentifier: bundle.identifier, |
| 355 | + path: "/documentation/FancyOverloads/overload3(_:)-\(hash)", |
| 356 | + sourceLanguage: .swift |
| 357 | + ) |
| 358 | + let symbol = try XCTUnwrap(context.entity(with: reference).semantic as? Symbol) |
| 359 | + var translator = RenderNodeTranslator( |
| 360 | + context: context, |
| 361 | + bundle: bundle, |
| 362 | + identifier: reference, |
| 363 | + source: nil |
| 364 | + ) |
| 365 | + let renderNode = try XCTUnwrap(translator.visitSymbol(symbol) as? RenderNode) |
| 366 | + let declarationsSection = try XCTUnwrap(renderNode.primaryContentSections.compactMap({ $0 as? DeclarationsRenderSection }).first) |
| 367 | + XCTAssertEqual(declarationsSection.declarations.count, 1) |
| 368 | + let declarations = try XCTUnwrap(declarationsSection.declarations.first) |
| 369 | + |
| 370 | + XCTAssert(declarations.tokens.allSatisfy({ $0.highlight == nil })) |
| 371 | + } |
| 372 | + } |
| 373 | +} |
| 374 | + |
| 375 | +/// Render a list of declaration tokens as a plain-text decoration and as a plain-text rendering of which characters are highlighted. |
| 376 | +func declarationAndHighlights(for tokens: [DeclarationRenderSection.Token]) -> [String] { |
| 377 | + [ |
| 378 | + tokens.map({ $0.text }).joined(), |
| 379 | + tokens.map({ String(repeating: $0.highlight == .changed ? "~" : " ", count: $0.text.count) }).joined() |
| 380 | + ] |
154 | 381 | }
|
0 commit comments