diff --git a/Sources/MockoloFramework/Models/ParsedEntity.swift b/Sources/MockoloFramework/Models/ParsedEntity.swift index 58d516b7..020e0ff5 100644 --- a/Sources/MockoloFramework/Models/ParsedEntity.swift +++ b/Sources/MockoloFramework/Models/ParsedEntity.swift @@ -65,12 +65,13 @@ struct ResolvedEntity { func model() -> Model { let metadata = entity.metadata + let combinedAttributes = entity.entityNode.attributeDescriptions + attributes return NominalModel(selfType: .init(name: metadata?.nameOverride ?? (key + "Mock")), namespaces: entity.entityNode.namespaces, acl: entity.entityNode.accessLevel, declKindOfMockAnnotatedBaseType: entity.entityNode.declKind, declKind: inheritsActorProtocol ? .actor : .class, - attributes: attributes, + attributes: combinedAttributes, offset: entity.entityNode.offset, inheritedTypeName: (entity.metadata?.module?.withDot ?? "") + key, genericWhereConstraints: entity.entityNode.genericWhereConstraints, @@ -91,7 +92,7 @@ protocol EntityNode { var nameText: String { get } var mayHaveGlobalActor: Bool { get } var accessLevel: String { get } - var attributesDescription: String { get } + var attributeDescriptions: [String] { get } var declKind: NominalTypeDeclKind { get } var inheritedTypes: [String] { get } var genericWhereConstraints: [String] { get } diff --git a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift index e4cf2798..016aff56 100644 --- a/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift +++ b/Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift @@ -303,8 +303,8 @@ extension ProtocolDeclSyntax: EntityNode { return genericWhereClause?.requirements.map { $0.with(\.trailingComma, nil).trimmedDescription } ?? [] } - var attributesDescription: String { - self.attributes.trimmedDescription + var attributeDescriptions: [String] { + return attributes.descriptions } func annotationMetadata(with annotation: String) -> AnnotationMetadata? { @@ -354,8 +354,8 @@ extension ClassDeclSyntax: EntityNode { return genericWhereClause?.requirements.map { $0.with(\.trailingComma, nil).trimmedDescription } ?? [] } - var attributesDescription: String { - self.attributes.trimmedDescription + var attributeDescriptions: [String] { + return attributes.descriptions } var isFinal: Bool { @@ -412,6 +412,15 @@ fileprivate func findNamespaces(parent: Syntax?) -> [String] { } extension AttributeListSyntax { + fileprivate var descriptions: [String] { + return compactMap { element in + guard case .attribute(let attribute) = element else { + return nil + } + return attribute.trimmedDescription + } + } + fileprivate var mayHaveGlobalActor: Bool { let wellKnownGlobalActor: Set = [.mainActor] return self.contains { element in diff --git a/Tests/TestActor/FixtureActor.swift b/Tests/TestActor/FixtureActor.swift index 9224d39d..77aff0f1 100644 --- a/Tests/TestActor/FixtureActor.swift +++ b/Tests/TestActor/FixtureActor.swift @@ -100,6 +100,7 @@ init() { } } + @available(iOS 18.0, *) class P1Mock: P1 { init() { } } diff --git a/Tests/TestSendable/FixtureSendable.swift b/Tests/TestSendable/FixtureSendable.swift index 59f128e4..863a5280 100644 --- a/Tests/TestSendable/FixtureSendable.swift +++ b/Tests/TestSendable/FixtureSendable.swift @@ -208,4 +208,76 @@ } } } + +@Fixture enum availableSendableProtocol { + @available(macOS 99.0, *) + struct Bar {} + + /// @mockable + @available(macOS 99.0, *) + protocol Foo: Sendable { + func bar() -> Bar + } + + @Fixture(includesConcurrencyHelpers: true) + enum expected { + @available(macOS 99.0, *) + final class FooMock: Foo, @unchecked Sendable { + init() { } + + + private let barState = MockoloMutex(MockoloHandlerState Bar>()) + var barCallCount: Int { + return barState.withLock(\.callCount) + } + var barHandler: (@Sendable () -> Bar)? { + get { barState.withLock(\.handler) } + set { barState.withLock { $0.handler = newValue } } + } + func bar() -> Bar { + let barHandler = barState.withLock { state in + state.callCount += 1 + return state.handler + } + if let barHandler = barHandler { + return barHandler() + } + fatalError("barHandler returns can't have a default value thus its handler must be set") + } + } + } +} + +@Fixture enum availableInheritedProtocol { + @available(macOS 100.0, *) + struct Bar {} + + @available(macOS 90.0, *) + protocol Foo { + } + + /// @mockable + @available(macOS 100.0, *) + protocol Foo2: Foo { + var bar: Bar { get set } + } + + @Fixture enum expected { + @available(macOS 100.0, *) + class Foo2Mock: Foo2 { + init() { } + init(bar: Bar) { + self._bar = bar + } + + + private(set) var barSetCallCount = 0 + private var _bar: Bar! { didSet { barSetCallCount += 1 } } + var bar: Bar { + get { return _bar } + set { _bar = newValue } + } + } + } +} #endif diff --git a/Tests/TestSendable/SendableTests.swift b/Tests/TestSendable/SendableTests.swift index 4bd7f1a0..beef3dfb 100644 --- a/Tests/TestSendable/SendableTests.swift +++ b/Tests/TestSendable/SendableTests.swift @@ -21,5 +21,15 @@ class SendableTests: MockoloTestCase { verify(srcContent: confirmedSendableProtocol._source, dstContent: confirmedSendableProtocol.expected._source) } + + func testAvailableSendableProtocol() { + verify(srcContent: availableSendableProtocol._source, + dstContent: availableSendableProtocol.expected._source) + } + + func testAvailableInheritedProtocol() { + verify(srcContent: availableInheritedProtocol._source, + dstContent: availableInheritedProtocol.expected._source) + } } #endif