Skip to content
7 changes: 6 additions & 1 deletion Sources/MockoloFramework/Models/ParsedEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,17 @@ struct ResolvedEntity {

func model() -> Model {
let metadata = entity.metadata
// Combine protocol-level attributes with member-level attributes
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Combine protocol-level attributes with member-level attributes

The meaning feels a bit off and it’s redundant, so let's remove it.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3e51751.
Removed the redundant comment.

let protocolLevelAttributes =
entity.entityNode.attributesDescription.isEmpty
? [] : [entity.entityNode.attributesDescription]
let combinedAttributes = protocolLevelAttributes + 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,
Expand Down
3 changes: 3 additions & 0 deletions Tests/TestActor/FixtureActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
init() { }
}

@MainActor
/// @mockable
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange for @mockable to come along too.
How about just inheriting available?
You can change EntityNode.attributesDescription requirement to var attributes: [String] { get }.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7f3a38b.
I changed declaration attribute handling to keep syntax attributes as separate entries, so @mockable trivia no longer gets carried along with @available.

@available(iOS 18.0, *)
class P1Mock: P1 {
init() { }
}
Expand Down
36 changes: 36 additions & 0 deletions Tests/TestSendable/FixtureSendable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,40 @@
}
}
}

@Fixture enum availableSendableProtocol {
/// @mockable
@available(iOS 18.0, *)
public protocol Foo: Sendable {
func bar() -> String
}
Comment on lines +216 to +220
Copy link
Copy Markdown
Collaborator

@sidepelican sidepelican Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to enforce a compilation error when @available isn't applied to a mock.

Suggested change
/// @mockable
@available(iOS 18.0, *)
public protocol Foo: Sendable {
func bar() -> String
}
@available(macOS 99.0, *)
struct Bar {}
/// @mockable
@available(macOS 99.0, *)
protocol Foo: Sendable {
func bar() -> Bar
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2fa6c3d.
I updated the fixture to use an availability-gated return type (Bar) so the generated mock must inherit @available to compile.


@Fixture(includesConcurrencyHelpers: true)
enum expected {
@available(iOS 18.0, *)
public final class FooMock: Foo, @unchecked Sendable {
public init() { }


private let barState = MockoloMutex(MockoloHandlerState<Never, @Sendable () -> String>())
public var barCallCount: Int {
return barState.withLock(\.callCount)
}
public var barHandler: (@Sendable () -> String)? {
get { barState.withLock(\.handler) }
set { barState.withLock { $0.handler = newValue } }
}
public func bar() -> String {
let barHandler = barState.withLock { state in
state.callCount += 1
return state.handler
}
if let barHandler = barHandler {
return barHandler()
}
return ""
}
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need a test case for protocol inheritance scenarios:

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 32dffb1.

}
#endif
5 changes: 5 additions & 0 deletions Tests/TestSendable/SendableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@ class SendableTests: MockoloTestCase {
verify(srcContent: confirmedSendableProtocol._source,
dstContent: confirmedSendableProtocol.expected._source)
}

func testAvailableSendableProtocol() {
verify(srcContent: availableSendableProtocol._source,
dstContent: availableSendableProtocol.expected._source)
}
}
#endif