diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 5a877bb..b6e8af2 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -12,16 +12,19 @@ concurrency: cancel-in-progress: true env: - XCODE_VERSION: "16.3" + XCODE_VERSION: "26.1" jobs: prepare: - runs-on: macos-15 + runs-on: macos-26 outputs: platforms: ${{ steps.platforms.outputs.platforms }} scheme: ${{ steps.scheme.outputs.scheme }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 + with: + submodules: true + - name: Setup Xcode run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app @@ -31,6 +34,7 @@ jobs: run: | curl https://mise.run | sh mise install + - name: Run linters run: mise lint @@ -62,13 +66,16 @@ jobs: build-and-test: needs: prepare - runs-on: macos-15 + runs-on: macos-26 strategy: fail-fast: false matrix: platform: ${{ fromJSON(needs.prepare.outputs.platforms) }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 + with: + submodules: true + - name: Setup Xcode run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app @@ -81,16 +88,16 @@ jobs: destination="platform=macOS,variant=Mac Catalyst" ;; ios) - destination="platform=iOS Simulator,name=iPhone 16 Pro Max,OS=latest" + destination="platform=iOS Simulator,name=iPhone 17 Pro Max,OS=26.1" ;; tvos) - destination="platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=latest" + destination="platform=tvOS Simulator,name=Apple TV 4K (3rd generation),OS=26.1" ;; watchos) - destination="platform=watchOS Simulator,name=Apple Watch Series 10 (46mm),OS=latest" + destination="platform=watchOS Simulator,name=Apple Watch Series 11 (46mm),OS=26.1" ;; visionos) - destination="platform=visionOS Simulator,name=Apple Vision Pro,OS=latest" + destination="platform=visionOS Simulator,name=Apple Vision Pro,OS=26.1" ;; *) echo "Unknown platform: ${{ matrix.platform }}" @@ -109,7 +116,8 @@ jobs: set -o pipefail xcodebuild build \ -scheme ${{ needs.prepare.outputs.scheme }} \ - -destination "${{ steps.destination.outputs.destination }}" | \ + -destination "${{ steps.destination.outputs.destination }}" \ + -IDEPackageEnablePrebuilts=NO | \ xcbeautify --renderer github-actions - name: Test (SPM) @@ -129,11 +137,12 @@ jobs: set -o pipefail xcodebuild test \ -scheme ${{ needs.prepare.outputs.scheme }} \ - -destination "${{ steps.destination.outputs.destination }}" | \ + -destination "${{ steps.destination.outputs.destination }}" \ + -IDEPackageEnablePrebuilts=NO | \ xcbeautify --renderer github-actions - name: Check coverage (SPM) if: ${{ matrix.platform == 'macos' }} uses: codecov/codecov-action@v5 with: - fail_ci_if_error: true \ No newline at end of file + fail_ci_if_error: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d109093..b076f96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,15 +6,17 @@ on: - '*' env: - XCODE_VERSION: "16.3" + XCODE_VERSION: "26.1" jobs: release: - runs-on: macos-15 + runs-on: macos-26 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: + submodules: true fetch-depth: 0 + - name: Setup Xcode run: sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a3bcbd3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Macros/Dependencies/PrincipleMacros"] + path = Macros/Dependencies/PrincipleMacros + url = https://github.com/NSFatalError/PrincipleMacros diff --git a/.mise.toml b/.mise.toml index 513f2cf..62cb361 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,12 +1,10 @@ [vars] -sources = "Sources" -tests = "Tests" swiftlint = '~/.local/bin/mise x -- swiftlint' swiftformat = '~/.local/bin/mise x -- swiftformat' [tools] -swiftlint = "0.58.2" -swiftformat = "0.55.5" +swiftlint = "0.62.2" +swiftformat = "0.58.5" [tasks.lint] description = 'Run all linters' @@ -19,7 +17,7 @@ run = """ {{ vars.swiftlint }} lint \ --config .swiftlint.yml \ --strict \ -{{ vars.sources }} +Sources Macros """ [tasks."swiftlint:tests"] @@ -30,7 +28,7 @@ run = """ --config .swiftlint.yml \ --config .swiftlint.tests.yml \ --strict \ -{{ vars.tests }} +Tests """ [tasks.swiftformat] diff --git a/.swift-version b/.swift-version index e8f1734..913671c 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -6.1 \ No newline at end of file +6.2 \ No newline at end of file diff --git a/.swiftformat b/.swiftformat index 25c4fbb..85fe404 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,106 +1,125 @@ --acronyms ID,URL,UUID --allman false ---anonymousforeach convert ---assetliterals visual-width ---asynccapturing ---beforemarks ---binarygrouping 4,8 ---callsiteparen default ---categorymark "MARK: %c" ---classthreshold 0 ---closingparen default ---closurevoid remove ---commas inline ---complexattrs prev-line ---computedvarattrs prev-line ---condassignment always ---conflictmarkers reject ---dateformat system ---decimalgrouping 3,6 ---doccomments before-declarations ---elseposition same-line ---emptybraces no-space ---enumnamespaces always ---enumthreshold 0 ---exponentcase lowercase ---exponentgrouping disabled ---extensionacl on-declarations ---extensionlength 0 ---extensionmark "MARK: - %t + %c" ---fractiongrouping disabled +--allow-partial-wrapping true +--anonymous-for-each convert +--asset-literals visual-width +--async-capturing +--before-marks +--binary-grouping 4,8 +--blank-line-after-switch-case multiline-only +--call-site-paren default +--category-mark "MARK: %c" +--class-threshold 0 +--closing-paren default +--closure-void remove +--complex-attributes prev-line +--computed-var-attributes prev-line +--conditional-assignment always +--conflict-markers reject +--date-format system +--decimal-grouping 3,6 +--default-test-suite-attributes +--doc-comments before-declarations +--else-position same-line +--empty-braces no-space +--enum-namespaces always +--enum-threshold 0 +--equatable-macro none +--exponent-case lowercase +--exponent-grouping disabled +--extension-acl on-declarations +--extension-mark "MARK: - %t + %c" +--extension-threshold 0 +--file-macro "#file" +--fraction-grouping disabled --fragment false ---funcattributes prev-line ---generictypes ---groupblanklines true ---groupedextension "MARK: %c" ---guardelse next-line +--func-attributes prev-line +--generic-types +--group-blank-lines true +--grouped-extension "MARK: %c" +--guard-else next-line --header ignore ---hexgrouping 4,8 ---hexliteralcase uppercase +--hex-grouping 4,8 +--hex-literal-case uppercase --ifdef indent ---importgrouping testable-first +--import-grouping testable-first --indent 4 ---indentcase false ---indentstrings false ---inferredtypes always ---initcodernil false ---inlinedforeach ignore +--indent-case false +--indent-strings false +--inferred-types always +--init-coder-nil false --lifecycle ---lineaftermarks true +--line-after-marks true +--line-between-guards false --linebreaks lf ---markcategories true ---markextensions always ---marktypes always ---maxwidth none ---modifierorder ---nevertrailing ---nilinit remove ---noncomplexattrs ---nospaceoperators ---nowrapoperators ---octalgrouping 4,8 ---operatorfunc spaced ---organizationmode visibility ---organizetypes actor,class,enum,struct ---patternlet hoist ---preservedecls ---preservedsymbols Package ---propertytypes inferred +--mark-categories true +--mark-class-threshold 0 +--mark-enum-threshold 0 +--mark-extension-threshold 0 +--mark-extensions always +--mark-struct-threshold 0 +--mark-types always +--markdown-files ignore +--max-width none +--modifier-order +--never-trailing +--nil-init remove +--no-space-operators +--no-wrap-operators +--non-complex-attributes +--octal-grouping 4,8 +--operator-func spaced +--organization-mode visibility +--organize-types actor,class,enum,struct +--pattern-let hoist +--preserve-acronyms +--preserve-decls +--preserved-property-types Package +--property-types inferred --ranges spaced +--redundant-async always +--redundant-throws always --self init-only ---selfrequired ---semicolons inline ---shortoptionals always ---smarttabs enabled ---someany true ---sortedpatterns ---storedvarattrs prev-line ---stripunusedargs always ---structthreshold 0 ---tabwidth unspecified ---throwcapturing +--self-required +--semicolons inline-only +--short-optionals always +--single-line-for-each ignore +--smart-tabs enabled +--some-any true +--sort-swiftui-properties none +--sorted-patterns +--stored-var-attributes prev-line +--strip-unused-args always +--struct-threshold 0 +--tab-width unspecified +--throw-capturing --timezone system ---trailingclosures ---trimwhitespace always ---typeattributes prev-line ---typeblanklines preserve ---typedelimiter space-after ---typemark "MARK: - %t" ---typemarks ---typeorder ---visibilitymarks ---visibilityorder ---voidtype void ---wraparguments before-first ---wrapcollections before-first ---wrapconditions after-first ---wrapeffects preserve ---wrapenumcases always ---wrapparameters before-first ---wrapreturntype preserve ---wrapternary before-operators ---wraptypealiases after-first ---xcodeindentation enabled ---yodaswap always ---disable enumNamespaces,fileHeader,headerFileName,redundantInternal,wrap,wrapMultilineStatementBraces,wrapSingleLineComments ---enable acronyms,blankLinesBetweenImports,blockComments,docComments,isEmpty,propertyTypes,redundantProperty,sortSwitchCases,unusedPrivateDeclarations,wrapConditionalBodies,wrapEnumCases +--trailing-closures +--trailing-commas never +--trim-whitespace always +--type-attributes prev-line +--type-blank-lines preserve +--type-body-marks preserve +--type-delimiter space-after +--type-mark "MARK: - %t" +--type-marks +--type-order +--url-macro none +--visibility-marks +--visibility-order +--void-type Void +--wrap-arguments before-first +--wrap-collections before-first +--wrap-conditions after-first +--wrap-effects preserve +--wrap-enum-cases always +--wrap-parameters before-first +--wrap-return-type preserve +--wrap-string-interpolation default +--wrap-ternary before-operators +--wrap-type-aliases after-first +--xcode-indentation enabled +--xctest-symbols +--yoda-swap always +--disable fileHeader,headerFileName,redundantInternal,wrap,wrapMultilineStatementBraces,wrapSingleLineComments +--enable acronyms,blankLinesBetweenImports,blockComments,docComments,emptyExtensions,environmentEntry,isEmpty,noForceTryInTests,noForceUnwrapInTests,noGuardInTests,propertyTypes,redundantAsync,redundantMemberwiseInit,redundantProperty,redundantThrows,singlePropertyPerLine,sortSwitchCases,unusedPrivateDeclarations,wrapConditionalBodies,wrapEnumCases,wrapMultilineFunctionChains diff --git a/.swiftlint.yml b/.swiftlint.yml index 186529f..c036114 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -7,7 +7,7 @@ opt_in_rules: - accessibility_trait_for_button - anonymous_argument_in_multiline_closure - array_init - # async_without_await - not recognized + - async_without_await # attributes # balanced_xctest_lifecycle # closure_body_length @@ -21,7 +21,7 @@ opt_in_rules: - contains_over_first_not_nil - contains_over_range_nil_comparison # contrasted_opening_brace - # convenience_type - not working with Testing framework + # convenience_type - direct_return - discarded_notification_center_observer - discouraged_assert @@ -56,6 +56,7 @@ opt_in_rules: - identical_operands - implicit_return # implicitly_unwrapped_optional + # incompatible_concurrency_annotation # indentation_width - joined_default_parameter - last_where @@ -66,7 +67,7 @@ opt_in_rules: - local_doc_comment - lower_acl_than_parent # missing_docs - # modifier_order + - modifier_order - multiline_arguments - multiline_arguments_brackets - multiline_function_chains @@ -90,6 +91,8 @@ opt_in_rules: - override_in_extension - pattern_matching_keywords - period_spacing + - prefer_asset_symbols + - prefer_condition_list - prefer_key_path # prefer_nimble - prefer_self_in_static_references @@ -123,7 +126,7 @@ opt_in_rules: - static_operator # strict_fileprivate - strong_iboutlet - - superfluous_else + # superfluous_else - switch_case_on_newline - test_case_accessibility - toggle_bool @@ -160,11 +163,14 @@ file_header: // Copyright © \d{4} .+\. All rights reserved\. // +excluded: + - Macros/Dependencies + file_length: warning: 500 identifier_name: - excluded: [id, x, y, z] + excluded: [id, ui, x, y, z, dx, dy, dz] line_length: ignores_comments: true @@ -172,6 +178,9 @@ line_length: nesting: type_level: 2 +no_magic_numbers: + allowed_numbers: [0.0, 1.0, 2.0, 100.0] + type_name: allowed_symbols: ["_"] max_length: 50 @@ -183,11 +192,11 @@ custom_rules: global_actor_attribute_order: name: "Global actor attribute order" message: "Global actor should be the first attribute." - regex: "(?-s)(@.+[^,\\s]\\s+@.*Actor)" + regex: "(?-s)(@.+[^,\\s]\\s+@.*Actor\\s)" sendable_attribute_order: name: "Sendable attribute order" message: "Sendable should be the first attribute." - regex: "(?-s)(@.+[^,\\s]\\s+@Sendable)" + regex: "(?-s)(@.+[^,\\s]\\s+@Sendable\\s)" autoclosure_attribute_order: name: "Autoclosure attribute order" message: "Autoclosure should be the last attribute." diff --git a/Macros/Dependencies/PrincipleMacros b/Macros/Dependencies/PrincipleMacros new file mode 160000 index 0000000..85aab93 --- /dev/null +++ b/Macros/Dependencies/PrincipleMacros @@ -0,0 +1 @@ +Subproject commit 85aab93496550f03b8888a96598fe06ad7685a53 diff --git a/Sources/DeeplyCopyableMacros/DeeplyCopyableEnumInitDeclBuilder.swift b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableEnumInitDeclBuilder.swift similarity index 84% rename from Sources/DeeplyCopyableMacros/DeeplyCopyableEnumInitDeclBuilder.swift rename to Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableEnumInitDeclBuilder.swift index 8dbc507..6418e37 100644 --- a/Sources/DeeplyCopyableMacros/DeeplyCopyableEnumInitDeclBuilder.swift +++ b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableEnumInitDeclBuilder.swift @@ -6,21 +6,17 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros -internal struct DeeplyCopyableEnumInitDeclBuilder: EnumDeclBuilder { +internal struct DeeplyCopyableEnumInitDeclBuilder: EnumDeclBuilder, MemberBuilding { let declaration: EnumDeclSyntax let cases: EnumCasesList - var settings: DeclBuilderSettings { - .init(accessControlLevel: .init(inheritingDeclaration: .member)) - } - func build() -> [DeclSyntax] { [ """ - \(inheritedAccessControlLevel)init(deeplyCopying other: \(trimmedTypeName)) { + \(inheritedAccessControlLevel)init(deeplyCopying other: \(trimmedType)) { \(switchExprBuilder().build()) } """ diff --git a/Sources/DeeplyCopyableMacros/DeeplyCopyableMacro.swift b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableMacro.swift similarity index 89% rename from Sources/DeeplyCopyableMacros/DeeplyCopyableMacro.swift rename to Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableMacro.swift index 2f38907..71aa2b5 100644 --- a/Sources/DeeplyCopyableMacros/DeeplyCopyableMacro.swift +++ b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableMacro.swift @@ -6,11 +6,11 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros public enum DeeplyCopyableMacro { - private enum ValidationResult { + private enum Input { case enumDecl(EnumDeclSyntax, cases: EnumCasesList) case statefulDecl(any StatefulDeclSyntax, filteredProperties: PropertiesList) @@ -19,7 +19,7 @@ public enum DeeplyCopyableMacro { private static func validate( _ declaration: some DeclGroupSyntax, in context: some MacroExpansionContext - ) -> ValidationResult? { + ) -> Input? { if let declaration = declaration as? EnumDeclSyntax { let cases = EnumCasesParser.parse( memberBlock: declaration.memberBlock, @@ -31,12 +31,13 @@ public enum DeeplyCopyableMacro { if let declaration = declaration as? (any StatefulDeclSyntax), declaration.isFinal { let filteredProperties = PropertiesParser .parse(memberBlock: declaration.memberBlock, in: context) - .stored.instance + .stored + .instance - for property in filteredProperties { + for property in filteredProperties.all { guard property.mutability == .mutable || property.binding.initializer == nil else { context.diagnose( - node: property.declaration, + node: property.underlying, errorMessage: "DeeplyCopyable properties must be settable from initializer" ) return nil @@ -66,11 +67,11 @@ extension DeeplyCopyableMacro: MemberMacro { conformingTo _: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [DeclSyntax] { - guard let result = validate(declaration, in: context) else { + guard let input = validate(declaration, in: context) else { return [] } - let builder: (any TypeDeclBuilder)? = switch result { + let builder: (any TypeDeclBuilder)? = switch input { case let .enumDecl(declaration, cases): DeeplyCopyableEnumInitDeclBuilder( declaration: declaration, @@ -98,11 +99,11 @@ extension DeeplyCopyableMacro: ExtensionMacro { conformingTo _: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [ExtensionDeclSyntax] { - guard let result = validate(declaration, in: context) else { + guard let input = validate(declaration, in: context) else { return [] } - let builder: DeeplyCopyableStatefulInitDeclBuilder? = switch result { + let builder: DeeplyCopyableStatefulInitDeclBuilder? = switch input { case let .statefulDecl(declaration as StructDeclSyntax, filteredProperties): DeeplyCopyableStatefulInitDeclBuilder( declaration: declaration, diff --git a/Sources/DeeplyCopyableMacros/DeeplyCopyableStatefulInitDeclBuilder.swift b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableStatefulInitDeclBuilder.swift similarity index 76% rename from Sources/DeeplyCopyableMacros/DeeplyCopyableStatefulInitDeclBuilder.swift rename to Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableStatefulInitDeclBuilder.swift index d6ae6b2..14341ea 100644 --- a/Sources/DeeplyCopyableMacros/DeeplyCopyableStatefulInitDeclBuilder.swift +++ b/Macros/ProbingMacros/DeeplyCopyable/DeeplyCopyableStatefulInitDeclBuilder.swift @@ -6,21 +6,17 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros -internal struct DeeplyCopyableStatefulInitDeclBuilder: StatefulDeclBuilder { +internal struct DeeplyCopyableStatefulInitDeclBuilder: StatefulDeclBuilder, MemberBuilding { let declaration: any StatefulDeclSyntax let filteredProperties: PropertiesList - var settings: DeclBuilderSettings { - .init(accessControlLevel: .init(inheritingDeclaration: .member)) - } - func build() -> [DeclSyntax] { [ """ - \(inheritedAccessControlLevel)init(deeplyCopying other: \(trimmedTypeName)) { + \(inheritedAccessControlLevel)init(deeplyCopying other: \(trimmedType)) { \(assignments().formatted()) } """ @@ -29,7 +25,7 @@ internal struct DeeplyCopyableStatefulInitDeclBuilder: StatefulDeclBuilder { @CodeBlockItemListBuilder private func assignments() -> CodeBlockItemListSyntax { - for property in filteredProperties { + for property in filteredProperties.all { "self.\(property.trimmedName) = other.\(property.trimmedName).deepCopy()" } } diff --git a/Sources/EquatableObjectMacros/EquatableDeclBuilder.swift b/Macros/ProbingMacros/EquatableObject/EquatableDeclBuilder.swift similarity index 69% rename from Sources/EquatableObjectMacros/EquatableDeclBuilder.swift rename to Macros/ProbingMacros/EquatableObject/EquatableDeclBuilder.swift index 5bdbedf..b8eeb8c 100644 --- a/Sources/EquatableObjectMacros/EquatableDeclBuilder.swift +++ b/Macros/ProbingMacros/EquatableObject/EquatableDeclBuilder.swift @@ -6,21 +6,17 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros -internal struct EquatableDeclBuilder: ClassDeclBuilder { +internal struct EquatableDeclBuilder: ClassDeclBuilder, MemberBuilding { let declaration: ClassDeclSyntax let properties: PropertiesList - var settings: DeclBuilderSettings { - .init(accessControlLevel: .init(inheritingDeclaration: .member)) - } - func build() -> [DeclSyntax] { [ """ - \(inheritedAccessControlLevel)static func == (lhs: \(trimmedTypeName), rhs: \(trimmedTypeName)) -> Bool { + \(inheritedAccessControlLevel)static func == (lhs: \(trimmedType), rhs: \(trimmedType)) -> Bool { \(equalityChecks().formatted()) return true } @@ -30,7 +26,7 @@ internal struct EquatableDeclBuilder: ClassDeclBuilder { @CodeBlockItemListBuilder private func equalityChecks() -> CodeBlockItemListSyntax { - for property in properties.stored.instance { + for property in properties.stored.instance.all { let name = property.trimmedName "guard lhs.\(name) == rhs.\(name) else { return false }" } diff --git a/Sources/EquatableObjectMacros/EquatableObjectMacro.swift b/Macros/ProbingMacros/EquatableObject/EquatableObjectMacro.swift similarity index 95% rename from Sources/EquatableObjectMacros/EquatableObjectMacro.swift rename to Macros/ProbingMacros/EquatableObject/EquatableObjectMacro.swift index a5efd18..e962b43 100644 --- a/Sources/EquatableObjectMacros/EquatableObjectMacro.swift +++ b/Macros/ProbingMacros/EquatableObject/EquatableObjectMacro.swift @@ -6,7 +6,7 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros public enum EquatableObjectMacro { @@ -34,7 +34,7 @@ extension EquatableObjectMacro: MemberMacro { providingMembersOf declaration: some DeclGroupSyntax, conformingTo _: [TypeSyntax], in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { + ) -> [DeclSyntax] { guard let declaration = validate(declaration, in: context) else { return [] } @@ -61,7 +61,7 @@ extension EquatableObjectMacro: ExtensionMacro { providingExtensionsOf type: some TypeSyntaxProtocol, conformingTo _: [TypeSyntax], in context: some MacroExpansionContext - ) throws -> [ExtensionDeclSyntax] { + ) -> [ExtensionDeclSyntax] { guard validate(declaration, in: context) != nil else { return [] } diff --git a/Sources/ProbingMacros/ProbingPlugin.swift b/Macros/ProbingMacros/Main/ProbingPlugin.swift similarity index 72% rename from Sources/ProbingMacros/ProbingPlugin.swift rename to Macros/ProbingMacros/Main/ProbingPlugin.swift index 03f0dd1..e9aee19 100644 --- a/Sources/ProbingMacros/ProbingPlugin.swift +++ b/Macros/ProbingMacros/Main/ProbingPlugin.swift @@ -6,14 +6,16 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros import SwiftCompilerPlugin +import SwiftSyntaxMacros @main internal struct ProbingPlugin: CompilerPlugin { let providingMacros: [any Macro.Type] = [ ProbeMacro.self, - EffectMacro.self + EffectMacro.self, + EquatableObjectMacro.self, + DeeplyCopyableMacro.self ] } diff --git a/Sources/ProbingMacros/EffectMacro.swift b/Macros/ProbingMacros/Probing/EffectMacro.swift similarity index 89% rename from Sources/ProbingMacros/EffectMacro.swift rename to Macros/ProbingMacros/Probing/EffectMacro.swift index 7a43cbb..abba28f 100644 --- a/Sources/ProbingMacros/EffectMacro.swift +++ b/Macros/ProbingMacros/Probing/EffectMacro.swift @@ -6,14 +6,11 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros -import SwiftSyntax +import SwiftSyntaxMacros public enum EffectMacro { static let name = "Effect" - static let concurrentName = "ConcurrentEffect" - static let allNames = [name, concurrentName] private static func isNested(lexicalContext: [Syntax]) -> Bool { lexicalContext.contains { syntax in @@ -57,6 +54,7 @@ extension EffectMacro: ExpressionMacro { ) #else return Task( + name: \(parameters.name), executorPreference: \(parameters.executorPreference), priority: \(parameters.priority), operation: \(rewriter.rewrite(parameters.operation, as: .task)) @@ -76,6 +74,7 @@ extension EffectMacro: ExpressionMacro { ) #else return Task( + name: \(parameters.name), priority: \(parameters.priority), operation: \(rewriter.rewrite(parameters.operation, as: .task)) ) @@ -104,14 +103,11 @@ extension EffectMacro { init(from node: some FreestandingMacroExpansionSyntax) throws { let extractor = ParameterExtractor(from: node) - let name = try extractor.expression(withLabel: nil) - let operation = try extractor.trailingClosure(withLabel: "operation") - let preprocessorFlag = (try? extractor.rawString(withLabel: "preprocessorFlag")) ?? "DEBUG" - let priority = (try? extractor.expression(withLabel: "priority")) ?? "nil" - - let executorPreference: ExprSyntax? = node.isConcurrentEffectMacro - ? "globalConcurrentExecutor" - : (try? extractor.expression(withLabel: "executorPreference")) + let name = try extractor.requiredExpression(withLabel: nil) + let operation = try extractor.requiredTrailingClosure(withLabel: "operation") + let preprocessorFlag = try extractor.rawString(withLabel: "preprocessorFlag") ?? "DEBUG" + let priority = extractor.expression(withLabel: "priority") ?? "nil" + let executorPreference = extractor.expression(withLabel: "executorPreference") self = if let executorPreference { .withExecutorPreference( @@ -196,6 +192,7 @@ extension EffectMacro { case let (.task, .withIsolatedOperation(parameters)): """ Task( + name: \(parameters.name), priority: \(parameters.priority), operation: \(rewrittenOperation) ) @@ -214,6 +211,7 @@ extension EffectMacro { case let (.task, .withExecutorPreference(parameters)): """ Task( + name: \(parameters.name), executorPreference: \(parameters.executorPreference), priority: \(parameters.priority), operation: \(rewrittenOperation) @@ -240,10 +238,6 @@ extension EffectMacro { extension FreestandingMacroExpansionSyntax { fileprivate var isEffectMacro: Bool { - EffectMacro.allNames.contains(macroName.trimmedDescription) - } - - fileprivate var isConcurrentEffectMacro: Bool { - EffectMacro.concurrentName == macroName.trimmedDescription + EffectMacro.name == macroName.trimmedDescription } } diff --git a/Sources/ProbingMacros/ProbeMacro.swift b/Macros/ProbingMacros/Probing/ProbeMacro.swift similarity index 71% rename from Sources/ProbingMacros/ProbeMacro.swift rename to Macros/ProbingMacros/Probing/ProbeMacro.swift index b687f17..5d742e7 100644 --- a/Sources/ProbingMacros/ProbeMacro.swift +++ b/Macros/ProbingMacros/Probing/ProbeMacro.swift @@ -6,7 +6,7 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -import PrincipleMacros +import SwiftSyntaxMacros public enum ProbeMacro: ExpressionMacro { @@ -14,7 +14,7 @@ public enum ProbeMacro: ExpressionMacro { of node: some FreestandingMacroExpansionSyntax, in _: some MacroExpansionContext ) throws -> ExprSyntax { - let parameters = Parameters(from: node) + let parameters = try Parameters(from: node) return """ { () async -> Void in #if \(raw: parameters.preprocessorFlag) @@ -35,10 +35,10 @@ extension ProbeMacro { let name: ExprSyntax let preprocessorFlag: String - init(from node: some FreestandingMacroExpansionSyntax) { + init(from node: some FreestandingMacroExpansionSyntax) throws { let extractor = ParameterExtractor(from: node) - self.name = (try? extractor.expression(withLabel: nil)) ?? ".default" - self.preprocessorFlag = (try? extractor.rawString(withLabel: "preprocessorFlag")) ?? "DEBUG" + self.name = extractor.expression(withLabel: nil) ?? ".default" + self.preprocessorFlag = try extractor.rawString(withLabel: "preprocessorFlag") ?? "DEBUG" } } } diff --git a/Package.resolved b/Package.resolved index 9051c7c..8cc27a7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,22 +1,13 @@ { - "originHash" : "2d6c8f9a49998eeeee25872d971d637bcc871153998138008826624e814a9650", + "originHash" : "fcc6194c26013caaa4e5670ef9cfd034f759eea98806b1cabaa9961acc9c6065", "pins" : [ { "identity" : "principle", "kind" : "remoteSourceControl", "location" : "https://github.com/NSFatalError/Principle", "state" : { - "revision" : "b7b16cbd21972a675a3543bae2f37aa323dd786b", - "version" : "1.0.3" - } - }, - { - "identity" : "principlemacros", - "kind" : "remoteSourceControl", - "location" : "https://github.com/NSFatalError/PrincipleMacros", - "state" : { - "revision" : "b2671db08bc28ee2336bd33517a524de4abeb92a", - "version" : "1.0.6" + "revision" : "3a14a1f47a3b4ef1df35a204e0aefc15024b9b0a", + "version" : "2.0.0" } }, { @@ -24,8 +15,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", - "version" : "601.0.1" + "revision" : "4799286537280063c85a32f09884cfbca301b1a1", + "version" : "602.0.0" } } ], diff --git a/Package.swift b/Package.swift index ccefc00..6a7ce69 100644 --- a/Package.swift +++ b/Package.swift @@ -1,55 +1,9 @@ -// swift-tools-version: 6.1 +// swift-tools-version: 6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import CompilerPluginSupport import PackageDescription -func macroTargets( - name: String, - dependencies: [Target.Dependency] = [], - testDependencies: [Target.Dependency] = [] -) -> [Target] { - [ - .target( - name: name, - dependencies: dependencies + [ - .target(name: "\(name)Macros") - ] - ), - .macro( - name: "\(name)Macros", - dependencies: [ - .product( - name: "PrincipleMacros", - package: "PrincipleMacros" - ), - .product( - name: "SwiftCompilerPlugin", - package: "swift-syntax" - ) - ] - ), - .testTarget( - name: "\(name)MacrosTests", - dependencies: [ - .target( - name: "\(name)Macros" - ), - .product( - name: "SwiftSyntaxMacrosTestSupport", - package: "swift-syntax" - ) - ] - ), - .testTarget( - name: "\(name)Tests", - dependencies: testDependencies + [ - .target(name: name) - ] - ) - ] -} - let package = Package( name: "Probing", platforms: [ @@ -73,23 +27,65 @@ let package = Package( dependencies: [ .package( url: "https://github.com/NSFatalError/Principle", - from: "1.0.3" - ), - .package( - url: "https://github.com/NSFatalError/PrincipleMacros", - from: "1.0.4" + from: "2.0.0" ), .package( url: "https://github.com/swiftlang/swift-syntax", - "600.0.0" ..< "602.0.0" + "602.0.0" ..< "603.0.0" ) ], targets: [ .target( - name: "ProbeTesting", + name: "Probing", dependencies: [ - "Probing" + "ProbingMacros", + .product( + name: "PrincipleConcurrency", + package: "Principle" + ), + .product( + name: "PrincipleCollections", + package: "Principle" + ) + ] + ), + .testTarget( + name: "ProbingTests", + dependencies: ["Probing"] + ), + + .macro( + name: "ProbingMacros", + dependencies: [ + .product( + name: "SwiftSyntaxMacros", + package: "swift-syntax" + ), + .product( + name: "SwiftCompilerPlugin", + package: "swift-syntax" + ) ], + path: "Macros", + sources: [ + "ProbingMacros/", + "Dependencies/PrincipleMacros/Sources/PrincipleMacros/" + ] + ), + .testTarget( + name: "ProbingMacrosTests", + dependencies: [ + "ProbingMacros", + .product( + name: "SwiftSyntaxMacrosTestSupport", + package: "swift-syntax" + ) + ] + ), + + .target( + name: "ProbeTesting", + dependencies: ["Probing"], swiftSettings: [ .enableExperimentalFeature("LifetimeDependence") ] @@ -103,32 +99,35 @@ let package = Package( package: "Principle" ) ] + ), + + .target( + name: "DeeplyCopyable", + dependencies: ["ProbingMacros"] + ), + .testTarget( + name: "DeeplyCopyableTests", + dependencies: [ + "DeeplyCopyable", + "EquatableObject" + ] + ), + + .target( + name: "EquatableObject", + dependencies: ["ProbingMacros"] + ), + .testTarget( + name: "EquatableObjectTests", + dependencies: ["EquatableObject"] ) - ] + macroTargets( - name: "Probing", - dependencies: [ - .product( - name: "PrincipleConcurrency", - package: "Principle" - ), - .product( - name: "PrincipleCollections", - package: "Principle" - ) - ] - ) + macroTargets( - name: "DeeplyCopyable", - testDependencies: [ - "EquatableObject" - ] - ) + macroTargets( - name: "EquatableObject" - ) + ] ) for target in package.targets { target.swiftSettings = (target.swiftSettings ?? []) + [ .swiftLanguageMode(.v6), - .enableUpcomingFeature("ExistentialAny") + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("MemberImportVisibility") ] } diff --git a/Sources/DeeplyCopyable/Protocols/DeeplyCopyable.swift b/Sources/DeeplyCopyable/Protocols/DeeplyCopyable.swift index 2343b98..e8adc47 100644 --- a/Sources/DeeplyCopyable/Protocols/DeeplyCopyable.swift +++ b/Sources/DeeplyCopyable/Protocols/DeeplyCopyable.swift @@ -22,7 +22,7 @@ names: named(init(deeplyCopying:)) ) public macro DeeplyCopyable() = #externalMacro( - module: "DeeplyCopyableMacros", + module: "ProbingMacros", type: "DeeplyCopyableMacro" ) diff --git a/Sources/DeeplyCopyableMacros/DeeplyCopyablePlugin.swift b/Sources/DeeplyCopyableMacros/DeeplyCopyablePlugin.swift deleted file mode 100644 index 9661d7c..0000000 --- a/Sources/DeeplyCopyableMacros/DeeplyCopyablePlugin.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// DeeplyCopyablePlugin.swift -// Probing -// -// Created by Kamil Strzelecki on 22/01/2025. -// Copyright © 2025 Kamil Strzelecki. All rights reserved. -// - -import PrincipleMacros -import SwiftCompilerPlugin - -@main -internal struct DeeplyCopyablePlugin: CompilerPlugin { - - let providingMacros: [any Macro.Type] = [ - DeeplyCopyableMacro.self - ] -} diff --git a/Sources/EquatableObject/EquatableObject.swift b/Sources/EquatableObject/EquatableObject.swift index faa4491..19d7785 100644 --- a/Sources/EquatableObject/EquatableObject.swift +++ b/Sources/EquatableObject/EquatableObject.swift @@ -19,6 +19,6 @@ conformances: Equatable ) public macro EquatableObject() = #externalMacro( - module: "EquatableObjectMacros", + module: "ProbingMacros", type: "EquatableObjectMacro" ) diff --git a/Sources/EquatableObjectMacros/EquatableObjectPlugin.swift b/Sources/EquatableObjectMacros/EquatableObjectPlugin.swift deleted file mode 100644 index d4ad0ec..0000000 --- a/Sources/EquatableObjectMacros/EquatableObjectPlugin.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// EquatableObjectPlugin.swift -// Probing -// -// Created by Kamil Strzelecki on 22/01/2025. -// Copyright © 2025 Kamil Strzelecki. All rights reserved. -// - -import PrincipleMacros -import SwiftCompilerPlugin - -@main -internal struct EquatableObjectPlugin: CompilerPlugin { - - let providingMacros: [any Macro.Type] = [ - EquatableObjectMacro.self - ] -} diff --git a/Sources/Probing/Documentation.docc/Probing.md b/Sources/Probing/Documentation.docc/Probing.md index 78999d9..1c38bd1 100644 --- a/Sources/Probing/Documentation.docc/Probing.md +++ b/Sources/Probing/Documentation.docc/Probing.md @@ -88,7 +88,6 @@ func callWithProbing() await { - ``Effect(_:preprocessorFlag:priority:operation:)`` - ``Effect(_:preprocessorFlag:executorPreference:priority:operation:)`` -- ``ConcurrentEffect(_:preprocessorFlag:priority:operation:)`` - ``EffectName`` - ``EffectIdentifier`` diff --git a/Sources/Probing/Effects/ConcurrentEffect.swift b/Sources/Probing/Effects/ConcurrentEffect.swift new file mode 100644 index 0000000..d11a677 --- /dev/null +++ b/Sources/Probing/Effects/ConcurrentEffect.swift @@ -0,0 +1,20 @@ +// +// ConcurrentEffect.swift +// Probing +// +// Created by Kamil Strzelecki on 16/11/2025. +// Copyright © 2025 Kamil Strzelecki. All rights reserved. +// + +@available(*, unavailable, message: "Use #Effect with @concurrent attribute instead") +@discardableResult +@freestanding(expression) +public macro ConcurrentEffect( + _ name: EffectName, + preprocessorFlag: StaticString = "DEBUG", + priority: TaskPriority? = nil, + operation: sending @escaping () async -> Success +) -> any Effect = #externalMacro( + module: "ProbingMacros", + type: "EffectMacro" +) diff --git a/Sources/Probing/Effects/Effect.swift b/Sources/Probing/Effects/Effect.swift index cb3d634..9aa19bc 100644 --- a/Sources/Probing/Effects/Effect.swift +++ b/Sources/Probing/Effects/Effect.swift @@ -35,7 +35,7 @@ @discardableResult @freestanding(expression) public macro Effect( - _ name: @autoclosure () -> EffectName, + _ name: EffectName, preprocessorFlag: StaticString = "DEBUG", priority: TaskPriority? = nil, @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success @@ -74,50 +74,9 @@ public macro Effect( @discardableResult @freestanding(expression) public macro Effect( - _ name: @autoclosure () -> EffectName, + _ name: EffectName, preprocessorFlag: StaticString = "DEBUG", // swiftformat:disable:next all - executorPreference taskExecutor: consuming (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping () async -> Success -) -> any Effect = #externalMacro( - module: "ProbingMacros", - type: "EffectMacro" -) - -/// Creates a `Task`-like effect that runs on the `globalConcurrentExecutor` and can be controlled from your tests. -/// -/// - Parameters: -/// - name: The name of the effect. It must be unique within the scope of its parent while the effect is still running. -/// - preprocessorFlag: A preprocessor flag that determines whether the generated code is included in the compiled binary. -/// Defaults to `DEBUG`. -/// - priority: The priority of the underlying task. -/// - operation: The asynchronous operation to perform. -/// -/// - Returns: An instance of type conforming to the ``Effect`` protocol. -/// -/// - Note: This macro is equivalent to calling ``Effect(_:preprocessorFlag:executorPreference:priority:operation:)`` with `globalConcurrentExecutor`. -/// If you were using `Task.detached`, this may be a viable alternative. -/// -/// When run in the `body` of `ProbeTesting.withProbing` function, the effect is suspended immediately after initialization, -/// instead of starting execution as `Task` would. Later, it can be resumed and suspended at suspension points declared -/// using the ``probe(_:preprocessorFlag:)`` macro within the `operation`. -/// -/// Each effect must be uniquely identified within the scope of its parent by its `name` at every point in its execution. -/// Failure to do so will result in an error during testing. Once an effect completes, its identifier can be reused. -/// -/// If your code is compiled with the given `preprocessorFlag`, the effect becomes accessible and controllable from your tests -/// only when created within the `body` of `ProbeTesting.withProbing` function. Outside of that scope, this call initializes -/// a regular Swift `Task` that is not subject to any additional scheduling. -/// -/// - Attention: Unlike `Task`, the ``Effect`` protocol does not support throwing errors. -/// Any error handling should be performed inside the operation executed by the effect itself. -/// This design choice helps prevent errors from being unintentionally left unhandled. -/// -@discardableResult -@freestanding(expression) -public macro ConcurrentEffect( - _ name: @autoclosure () -> EffectName, - preprocessorFlag: StaticString = "DEBUG", + executorPreference taskExecutor: (any TaskExecutor)?, priority: TaskPriority? = nil, operation: sending @escaping () async -> Success ) -> any Effect = #externalMacro( diff --git a/Sources/Probing/Effects/TestableEffect.swift b/Sources/Probing/Effects/TestableEffect.swift index 2c09a90..77d1937 100644 --- a/Sources/Probing/Effects/TestableEffect.swift +++ b/Sources/Probing/Effects/TestableEffect.swift @@ -19,7 +19,7 @@ public struct TestableEffect: Effect, Hashable { @discardableResult public static func _make( // swiftlint:disable:this identifier_name - _ name: @autoclosure () -> EffectName, + _ name: EffectName, priority: TaskPriority?, fileID: String = #fileID, line: Int = #line, @@ -29,15 +29,15 @@ public struct TestableEffect: Effect, Hashable { guard let coordinator = ProbingCoordinator.current else { return .init( task: Task( + name: name.rawValue, priority: priority, operation: operation ) ) } - let name = name() - let id = EffectIdentifier.current(appending: name) let isolation = extractIsolation(operation) + let id = EffectIdentifier.current(appending: name) var transfer = SingleUseTransfer(operation) let location = ProbingLocation( fileID: fileID, @@ -48,6 +48,7 @@ public struct TestableEffect: Effect, Hashable { guard coordinator.willCreateEffect(withID: id, at: location) else { return .init( task: Task( + name: name.rawValue, priority: priority, operation: transfer.finalize() ) @@ -57,7 +58,7 @@ public struct TestableEffect: Effect, Hashable { let task = EffectIdentifier.withChild(id) { var transfer = transfer.take() - return Task(priority: priority) { + return Task(name: name.rawValue, priority: priority) { // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0472-task-start-synchronously-on-caller-context.md // https://forums.swift.org/t/closure-isolation-control/70378 @@ -83,8 +84,8 @@ public struct TestableEffect: Effect, Hashable { @discardableResult public static func _make( // swiftlint:disable:this identifier_name - _ name: @autoclosure () -> EffectName, - executorPreference taskExecutor: consuming (any TaskExecutor)?, + _ name: EffectName, + executorPreference taskExecutor: (any TaskExecutor)?, priority: TaskPriority?, fileID: String = #fileID, line: Int = #line, @@ -94,6 +95,7 @@ public struct TestableEffect: Effect, Hashable { guard let coordinator = ProbingCoordinator.current else { return .init( task: Task( + name: name.rawValue, executorPreference: taskExecutor, priority: priority, operation: operation @@ -101,7 +103,6 @@ public struct TestableEffect: Effect, Hashable { ) } - let name = name() let id = EffectIdentifier.current(appending: name) var transfer = SingleUseTransfer(operation) let location = ProbingLocation( @@ -113,6 +114,7 @@ public struct TestableEffect: Effect, Hashable { guard coordinator.willCreateEffect(withID: id, at: location) else { return .init( task: Task( + name: name.rawValue, executorPreference: taskExecutor, priority: priority, operation: transfer.finalize() @@ -123,7 +125,7 @@ public struct TestableEffect: Effect, Hashable { let task = EffectIdentifier.withChild(id) { [taskExecutor] in var transfer = transfer.take() - return Task(executorPreference: taskExecutor, priority: priority) { + return Task(name: name.rawValue, executorPreference: taskExecutor, priority: priority) { await coordinator.willStartEffect(withID: id, isolation: nil) let value = await transfer.finalize()() @@ -142,3 +144,44 @@ public struct TestableEffect: Effect, Hashable { return .init(task: task) } } + +extension TestableEffect { + + private static func extractIsolation( + _ operation: @escaping @isolated(any) () async throws -> Success + ) -> (any Actor)? { + operation.isolation + } +} + +extension Task +where Failure == Never { + + @discardableResult + public init( + name: EffectName, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success + ) { + self.init( + name: name.rawValue, + priority: priority, + operation: operation + ) + } + + @discardableResult + public init( + name: EffectName, + executorPreference taskExecutor: (any TaskExecutor)?, + priority: TaskPriority? = nil, + operation: sending @escaping () async -> Success + ) { + self.init( + name: name.rawValue, + executorPreference: taskExecutor, + priority: priority, + operation: operation + ) + } +} diff --git a/Sources/Probing/Probes/Probe.swift b/Sources/Probing/Probes/Probe.swift index f386b26..bbfbd04 100644 --- a/Sources/Probing/Probes/Probe.swift +++ b/Sources/Probing/Probes/Probe.swift @@ -1,3 +1,4 @@ +// swiftlint:disable:this file_name // // Probe.swift // Probing diff --git a/Tests/DeeplyCopyableTests/DeeplyCopyableTests.swift b/Tests/DeeplyCopyableTests/DeeplyCopyableTests.swift index c511c7a..8877919 100644 --- a/Tests/DeeplyCopyableTests/DeeplyCopyableTests.swift +++ b/Tests/DeeplyCopyableTests/DeeplyCopyableTests.swift @@ -14,7 +14,7 @@ import Testing internal struct DeeplyCopyableTests { @Test - func testData() { + func data() { var data = Data([0, 1, 2, 3]) let copy = data.deepCopy() #expect(data == copy) @@ -24,7 +24,7 @@ internal struct DeeplyCopyableTests { } @Test - func testString() throws { + func string() throws { var string = "string" let copy = string.deepCopy() #expect(string == copy) @@ -35,7 +35,7 @@ internal struct DeeplyCopyableTests { } @Test - func testSubstring() throws { + func substring() throws { let string = "string" var substring = string[...] let copy = substring.deepCopy() @@ -47,7 +47,7 @@ internal struct DeeplyCopyableTests { } @Test - func testArray() { + func array() { let person = Person(id: 1) let array = [person, Person(id: 2)] let copy = array.deepCopy() @@ -58,7 +58,7 @@ internal struct DeeplyCopyableTests { } @Test - func testArraySlice() { + func arraySlice() { let person = Person(id: 1) let array = [person, Person(id: 2)] let slice = array[...] @@ -70,7 +70,7 @@ internal struct DeeplyCopyableTests { } @Test - func testSet() { + func set() { let person = Person(id: 1) let set: Set = [person, Person(id: 2)] let copy = set.deepCopy() @@ -81,7 +81,7 @@ internal struct DeeplyCopyableTests { } @Test - func testDictionary() { + func dictionary() { let person = Person(id: 1) let dictionary = ["a": person, "b": Person(id: 2)] let copy = dictionary.deepCopy() @@ -92,7 +92,7 @@ internal struct DeeplyCopyableTests { } @Test - func testRange() { + func range() { let person = Person(id: 0) let range = person ..< Person(id: 1) let copy = range.deepCopy() @@ -103,7 +103,7 @@ internal struct DeeplyCopyableTests { } @Test - func testClosedRange() { + func closedRange() { let person = Person(id: 0) let range = person ... Person(id: 1) let copy = range.deepCopy() @@ -114,7 +114,7 @@ internal struct DeeplyCopyableTests { } @Test - func testPartialRangeFrom() { + func partialRangeFrom() { let person = Person(id: 0) let range = person... let copy = range.deepCopy() @@ -125,7 +125,7 @@ internal struct DeeplyCopyableTests { } @Test - func testPartialRangeThrough() { + func partialRangeThrough() { let person = Person(id: 0) let range = ...person let copy = range.deepCopy() @@ -136,7 +136,7 @@ internal struct DeeplyCopyableTests { } @Test - func testPartialRangeUpTo() { + func partialRangeUpTo() { let person = Person(id: 0) let range = .. Bool { + lhs.id < rhs.id + } + func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(age) hasher.combine(name) hasher.combine(surname) } - - static func < (lhs: Person, rhs: Person) -> Bool { - lhs.id < rhs.id - } } } diff --git a/Tests/EquatableObjectTests/EquatableObjectTests.swift b/Tests/EquatableObjectTests/EquatableObjectTests.swift index a4cd9ef..aa50b10 100644 --- a/Tests/EquatableObjectTests/EquatableObjectTests.swift +++ b/Tests/EquatableObjectTests/EquatableObjectTests.swift @@ -12,14 +12,14 @@ import Testing internal struct EquatableObjectTests { @Test - func testEquality() { + func equality() { let lhs = Person() let rhs = Person() #expect(lhs == rhs) } @Test - func testNonEquality() { + func nonEquality() { let lhs = Person(id: 0) let rhs = Person(id: 1) #expect(lhs != rhs) diff --git a/Tests/ProbeTestingTests/Suites/APIMisuseTests.swift b/Tests/ProbeTestingTests/Suites/APIMisuseTests.swift index 82d531d..ffbacb5 100644 --- a/Tests/ProbeTestingTests/Suites/APIMisuseTests.swift +++ b/Tests/ProbeTestingTests/Suites/APIMisuseTests.swift @@ -15,7 +15,7 @@ internal struct APIMisuseTests { private let interactor = NonSendableInteractor() @Test - func testInstallingProbeInTask() async throws { + func installingProbeInTask() async throws { try await withKnownIssue(isIntermittent: true) { try await withProbing(options: .attemptProbingInTasks) { interactor.callWithProbeInTask() @@ -29,7 +29,7 @@ internal struct APIMisuseTests { } @Test - func testInstallingProbesInTaskGroup() async throws { + func installingProbesInTaskGroup() async throws { try await withKnownIssue(isIntermittent: true) { try await withProbing(options: .attemptProbingInTasks) { await interactor.callWithProbesInTaskGroup() @@ -44,7 +44,7 @@ internal struct APIMisuseTests { } @Test - func testInstallingProbesInAsyncLet() async throws { + func installingProbesInAsyncLet() async throws { try await withKnownIssue(isIntermittent: true) { try await withProbing(options: .attemptProbingInTasks) { await interactor.callWithProbesInAsyncLet() @@ -62,7 +62,7 @@ internal struct APIMisuseTests { extension APIMisuseTests { @Test - func testCreatingEffectInTask() async throws { + func creatingEffectInTask() async throws { try await withKnownIssue(isIntermittent: true) { try await withProbing(options: .attemptProbingInTasks) { interactor.callWithEffectInTask() @@ -76,7 +76,7 @@ extension APIMisuseTests { } @Test - func testCreatingEffectAfterInstallingProbeInTaskGroup() async throws { + func creatingEffectAfterInstallingProbeInTaskGroup() async throws { try await withKnownIssue(isIntermittent: true) { try await withProbing(options: .attemptProbingInTasks) { await interactor.callWithProbeAndEffectInTaskGroup() diff --git a/Tests/ProbeTestingTests/Suites/AsyncSequenceTests.swift b/Tests/ProbeTestingTests/Suites/AsyncSequenceTests.swift index 51e5890..24e4f85 100644 --- a/Tests/ProbeTestingTests/Suites/AsyncSequenceTests.swift +++ b/Tests/ProbeTestingTests/Suites/AsyncSequenceTests.swift @@ -21,7 +21,7 @@ internal struct AsyncSequenceTests { } @Test - func testRunningThroughAsyncStream() async throws { + func runningThroughAsyncStream() async throws { try await withProbing { await interactor.callWithAsyncStream() } dispatchedBy: { dispatcher in @@ -46,7 +46,7 @@ internal struct AsyncSequenceTests { } @Test - func testRunningThroughAsyncStreamInEffect() async throws { + func runningThroughAsyncStreamInEffect() async throws { try await withProbing { await interactor.callWithAsyncStreamInEffect() } dispatchedBy: { dispatcher in @@ -80,7 +80,7 @@ internal struct AsyncSequenceTests { extension AsyncSequenceTests { @Test - func testRunningUpToProbe() async throws { + func runningUpToProbe() async throws { try await withProbing { await interactor.callWithAsyncStream() } dispatchedBy: { dispatcher in @@ -92,7 +92,7 @@ extension AsyncSequenceTests { } @Test - func testRunningUpToNamedProbe() async throws { + func runningUpToNamedProbe() async throws { try await withProbing { await interactor.callWithAsyncStream() } dispatchedBy: { dispatcher in @@ -104,7 +104,7 @@ extension AsyncSequenceTests { } @Test - func testRunningUpToProbeInEffect() async throws { + func runningUpToProbeInEffect() async throws { try await withProbing { await interactor.callWithAsyncStreamInEffect() } dispatchedBy: { dispatcher in @@ -119,7 +119,7 @@ extension AsyncSequenceTests { extension AsyncSequenceTests { @Test - func testRunningUntilExitOfBody() async throws { + func runningUntilExitOfBody() async throws { try await withProbing { await interactor.callWithAsyncStream() } dispatchedBy: { dispatcher in @@ -131,7 +131,7 @@ extension AsyncSequenceTests { } @Test - func testRunningUntilEverythingCompleted() async throws { + func runningUntilEverythingCompleted() async throws { try await withProbing { await interactor.callWithAsyncStreamInEffect() } dispatchedBy: { dispatcher in @@ -143,7 +143,7 @@ extension AsyncSequenceTests { } @Test - func testRunningUntilEffectCompleted() async throws { + func runningUntilEffectCompleted() async throws { try await withProbing { await interactor.callWithAsyncStreamInEffect() } dispatchedBy: { dispatcher in diff --git a/Tests/ProbeTestingTests/Suites/EffectTests.swift b/Tests/ProbeTestingTests/Suites/EffectTests.swift index 1ee02f8..a691c57 100644 --- a/Tests/ProbeTestingTests/Suites/EffectTests.swift +++ b/Tests/ProbeTestingTests/Suites/EffectTests.swift @@ -21,7 +21,7 @@ internal class EffectTests { } @Test - func testNameAmbiguity() async throws { + func nameAmbiguity() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithAmbiguousEffects() @@ -34,7 +34,7 @@ internal class EffectTests { } @Test - func testNameAmbiguityWhenChildNotCompleted() async throws { + func nameAmbiguityWhenChildNotCompleted() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithAmbiguousEffects() @@ -48,7 +48,7 @@ internal class EffectTests { } @Test - func testNameReplacement() async throws { + func nameReplacement() async throws { try await withProbing { await interactor.callWithAmbiguousEffects() } dispatchedBy: { dispatcher in @@ -67,7 +67,7 @@ internal class EffectTests { extension EffectTests { @Test - func testGettingValue() async throws { + func gettingValue() async throws { try await withProbing { await interactor.callWithEffect() } dispatchedBy: { dispatcher in @@ -78,7 +78,7 @@ extension EffectTests { } @Test - func testGettingValueWhenCancelled() async throws { + func gettingValueWhenCancelled() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithCancelledEffect() @@ -92,7 +92,7 @@ extension EffectTests { } @Test - func testGettingValueWhenCastingFails() async throws { + func gettingValueWhenCastingFails() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithEffect() @@ -106,7 +106,7 @@ extension EffectTests { } @Test - func testGettingValueWhenNotCompleted() async throws { + func gettingValueWhenNotCompleted() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithEffect() @@ -123,7 +123,7 @@ extension EffectTests { extension EffectTests { @Test - func testGettingCancelledValue() async throws { + func gettingCancelledValue() async throws { try await withProbing { await interactor.callWithCancelledEffect() } dispatchedBy: { dispatcher in @@ -134,7 +134,7 @@ extension EffectTests { } @Test - func testGettingCancelledValueWhenFinished() async throws { + func gettingCancelledValueWhenFinished() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithEffect() @@ -148,7 +148,7 @@ extension EffectTests { } @Test - func testGettingCancelledValueWhenCastingFails() async throws { + func gettingCancelledValueWhenCastingFails() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithCancelledEffect() @@ -162,7 +162,7 @@ extension EffectTests { } @Test - func testGettingCancelledValueWhenNotCompleted() async throws { + func gettingCancelledValueWhenNotCompleted() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithCancelledEffect() diff --git a/Tests/ProbeTestingTests/Suites/IndependentEffectsTests.swift b/Tests/ProbeTestingTests/Suites/IndependentEffectsTests.swift index 0b8c37c..b56f692 100644 --- a/Tests/ProbeTestingTests/Suites/IndependentEffectsTests.swift +++ b/Tests/ProbeTestingTests/Suites/IndependentEffectsTests.swift @@ -13,7 +13,7 @@ import Testing internal final class IndependentEffectsTests: EffectTests { @Test - func testRunningThroughProbes() async throws { + func runningThroughProbes() async throws { try await withProbing { await interactor.callWithIndependentEffects() } dispatchedBy: { dispatcher in @@ -96,7 +96,7 @@ internal final class IndependentEffectsTests: EffectTests { arguments: 1 ..< 3, 1 ..< 3 ) - func testRunningToProbe( + func runningToProbe( inEffect effect: Int, withNumber number: Int ) async throws { @@ -124,7 +124,7 @@ internal final class IndependentEffectsTests: EffectTests { } @Test - func testNameEnumeration() async throws { + func nameEnumeration() async throws { try await withProbing { await interactor.callWithIndependentEnumeratedEffects() } dispatchedBy: { dispatcher in @@ -141,7 +141,7 @@ internal final class IndependentEffectsTests: EffectTests { extension IndependentEffectsTests { @Test - func testRunningWithoutDispatches() async throws { + func runningWithoutDispatches() async throws { try await withProbing { await interactor.callWithIndependentEffects() } dispatchedBy: { _ in @@ -151,7 +151,7 @@ extension IndependentEffectsTests { } @Test - func testRunningUntilExitOfBody() async throws { + func runningUntilExitOfBody() async throws { try await withProbing { await interactor.callWithIndependentEffects() } dispatchedBy: { dispatcher in @@ -162,7 +162,7 @@ extension IndependentEffectsTests { } @Test - func testRunningUntilEverythingCompleted() async throws { + func runningUntilEverythingCompleted() async throws { try await withProbing { await interactor.callWithIndependentEffects() } dispatchedBy: { dispatcher in @@ -179,7 +179,7 @@ extension IndependentEffectsTests { } @Test - func testGettingMissingEffectValue() async throws { + func gettingMissingEffectValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithIndependentEffects() @@ -199,7 +199,7 @@ extension IndependentEffectsTests { } @Test - func testGettingMissingEffectCancelledValue() async throws { + func gettingMissingEffectCancelledValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithIndependentEffects() @@ -219,7 +219,7 @@ extension IndependentEffectsTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbe(options: ProbingOptions) async throws { + func runningUpToMissingProbe(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithIndependentEffects() @@ -238,7 +238,7 @@ extension IndependentEffectsTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbeInEffect(options: ProbingOptions) async throws { + func runningUpToMissingProbeInEffect(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithIndependentEffects() @@ -260,7 +260,7 @@ extension IndependentEffectsTests { arguments: [true, false], ProbingOptions.all ) - func testRunningUntilMissingEffectCompleted( + func runningUntilMissingEffectCompleted( includingDescendants: Bool, options: ProbingOptions ) async throws { diff --git a/Tests/ProbeTestingTests/Suites/NestedEffectsTests.swift b/Tests/ProbeTestingTests/Suites/NestedEffectsTests.swift index 9e6a8c1..09e8954 100644 --- a/Tests/ProbeTestingTests/Suites/NestedEffectsTests.swift +++ b/Tests/ProbeTestingTests/Suites/NestedEffectsTests.swift @@ -13,7 +13,7 @@ import Testing internal final class NestedEffectsTests: EffectTests { @Test - func testRunningThroughProbes() async throws { + func runningThroughProbes() async throws { try await withProbing { await interactor.callWithNestedEffects() } dispatchedBy: { dispatcher in @@ -96,7 +96,7 @@ internal final class NestedEffectsTests: EffectTests { arguments: product(1 ..< 3, 1 ..< 3), 1 ..< 3 ) - func testRunningToProbe( + func runningToProbe( inEffect effect: (parent: Int, child: Int), withNumber number: Int ) async throws { @@ -148,7 +148,7 @@ internal final class NestedEffectsTests: EffectTests { } @Test - func testNameEnumeration() async throws { + func nameEnumeration() async throws { try await withProbing { await interactor.callWithNestedEnumeratedEffects() } dispatchedBy: { dispatcher in @@ -189,7 +189,7 @@ internal final class NestedEffectsTests: EffectTests { extension NestedEffectsTests { @Test - func testRunningWithoutDispatches() async throws { + func runningWithoutDispatches() async throws { try await withProbing { await interactor.callWithNestedEffects() } dispatchedBy: { _ in @@ -199,7 +199,7 @@ extension NestedEffectsTests { } @Test - func testRunningUntilExitOfBody() async throws { + func runningUntilExitOfBody() async throws { try await withProbing { await interactor.callWithNestedEffects() } dispatchedBy: { dispatcher in @@ -210,7 +210,7 @@ extension NestedEffectsTests { } @Test - func testRunningUntilEverythingCompleted() async throws { + func runningUntilEverythingCompleted() async throws { try await withProbing { await interactor.callWithNestedEffects() } dispatchedBy: { dispatcher in @@ -231,7 +231,7 @@ extension NestedEffectsTests { } @Test - func testGettingMissingEffectValue() async throws { + func gettingMissingEffectValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithNestedEffects() @@ -251,7 +251,7 @@ extension NestedEffectsTests { } @Test - func testGettingMissingEffectCancelledValue() async throws { + func gettingMissingEffectCancelledValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithNestedEffects() @@ -271,7 +271,7 @@ extension NestedEffectsTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbe(options: ProbingOptions) async throws { + func runningUpToMissingProbe(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithNestedEffects() @@ -293,7 +293,7 @@ extension NestedEffectsTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbeInEffect(options: ProbingOptions) async throws { + func runningUpToMissingProbeInEffect(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithNestedEffects() @@ -318,7 +318,7 @@ extension NestedEffectsTests { arguments: [true, false], ProbingOptions.all ) - func testRunningUntilMissingEffectCompleted( + func runningUntilMissingEffectCompleted( includingDescendants: Bool, options: ProbingOptions ) async throws { @@ -356,7 +356,7 @@ extension EffectTests.IsolatedInteractor { await #probe("1") model.tick() - #ConcurrentEffect("2") { + #Effect("2") { @concurrent in await self.callWithIndependentEffects() } @@ -368,7 +368,7 @@ extension EffectTests.IsolatedInteractor { #Effect(.enumerated("name")) { self.callWithIndependentEnumeratedEffects() } - #ConcurrentEffect(.enumerated("name")) { + #Effect(.enumerated("name")) { @concurrent in await self.callWithIndependentEnumeratedEffects() } } diff --git a/Tests/ProbeTestingTests/Suites/ProbeTests.swift b/Tests/ProbeTestingTests/Suites/ProbeTests.swift index 9fdec25..c850c23 100644 --- a/Tests/ProbeTestingTests/Suites/ProbeTests.swift +++ b/Tests/ProbeTestingTests/Suites/ProbeTests.swift @@ -21,7 +21,7 @@ internal struct ProbeTests { } @Test - func testRunningThroughDefaultProbes() async throws { + func runningThroughDefaultProbes() async throws { try await withProbing { await interactor.callWithDefaultProbes() } dispatchedBy: { dispatcher in @@ -36,7 +36,7 @@ internal struct ProbeTests { } @Test - func testRunningThroughNamedProbes() async throws { + func runningThroughNamedProbes() async throws { try await withProbing { await interactor.callWithNamedProbes() } dispatchedBy: { dispatcher in @@ -51,7 +51,7 @@ internal struct ProbeTests { } @Test(arguments: 1 ..< 3) - func testRunningToNamedProbe(withNumber number: Int) async throws { + func runningToNamedProbe(withNumber number: Int) async throws { try await withProbing { await interactor.callWithNamedProbes() } dispatchedBy: { dispatcher in @@ -67,7 +67,7 @@ internal struct ProbeTests { extension ProbeTests { @Test - func testRunningWithoutDispatches() async throws { + func runningWithoutDispatches() async throws { try await withProbing { await interactor.callWithDefaultProbes() } dispatchedBy: { _ in @@ -77,7 +77,7 @@ extension ProbeTests { } @Test - func testRunningUntilExitOfBody() async throws { + func runningUntilExitOfBody() async throws { try await withProbing { await interactor.callWithDefaultProbes() } dispatchedBy: { dispatcher in @@ -88,7 +88,7 @@ extension ProbeTests { } @Test - func testRunningUntilEverythingCompleted() async throws { + func runningUntilEverythingCompleted() async throws { try await withProbing { await interactor.callWithDefaultProbes() } dispatchedBy: { dispatcher in @@ -99,7 +99,7 @@ extension ProbeTests { } @Test - func testGettingMissingEffectValue() async throws { + func gettingMissingEffectValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithDefaultProbes() @@ -119,7 +119,7 @@ extension ProbeTests { } @Test - func testGettingMissingEffectCancelledValue() async throws { + func gettingMissingEffectCancelledValue() async throws { try await withKnownIssue { try await withProbing { await interactor.callWithDefaultProbes() @@ -139,7 +139,7 @@ extension ProbeTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbe(options: ProbingOptions) async throws { + func runningUpToMissingProbe(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithNamedProbes() @@ -158,7 +158,7 @@ extension ProbeTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbeInEffect(options: ProbingOptions) async throws { + func runningUpToMissingProbeInEffect(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { await interactor.callWithNamedProbes() @@ -180,7 +180,7 @@ extension ProbeTests { arguments: [true, false], ProbingOptions.all ) - func testRunningUntilMissingEffectCompleted( + func runningUntilMissingEffectCompleted( includingDescendants: Bool, options: ProbingOptions ) async throws { @@ -208,7 +208,7 @@ extension ProbeTests { extension ProbeTests { @Test - func testThrowingLateInBody() async { + func throwingLateInBody() async { await #expect(throws: ErrorMock.self) { try await withProbing { await interactor.callWithDefaultProbes() @@ -221,7 +221,7 @@ extension ProbeTests { } @Test - func testThrowingLateInTest() async { + func throwingLateInTest() async { await #expect(throws: ErrorMock.self) { try await confirmation { confirmation in try await withProbing { diff --git a/Tests/ProbeTestingTests/Suites/ProbingOptionsTests.swift b/Tests/ProbeTestingTests/Suites/ProbingOptionsTests.swift index 2e3a0bd..def8ca6 100644 --- a/Tests/ProbeTestingTests/Suites/ProbingOptionsTests.swift +++ b/Tests/ProbeTestingTests/Suites/ProbingOptionsTests.swift @@ -22,7 +22,7 @@ internal struct ProbingOptionsTests { } @Test - func testAttemptingProbingInInTask() async throws { + func attemptingProbingInInTask() async throws { try await withProbing(options: .attemptProbingInTasks) { await interactor.callWithTask() } dispatchedBy: { dispatcher in @@ -64,7 +64,7 @@ extension ProbingOptionsTests { + ignoringEffectsInTasksArguments.map { ($0, ProbingErrors.ChildEffectNotCreated.self) } @Test(arguments: ignoringProbesInTasksArguments) - func testIgnoringProbesInTask(argument: Argument) async throws { + func ignoringProbesInTask(argument: Argument) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithTask() @@ -84,7 +84,7 @@ extension ProbingOptionsTests { } @Test(arguments: ignoringProbesInTasksArguments) - func testIgnoringProbesInTaskGroup(argument: Argument) async throws { + func ignoringProbesInTaskGroup(argument: Argument) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithTaskGroup() @@ -104,7 +104,7 @@ extension ProbingOptionsTests { } @Test(arguments: ignoringProbesInTasksArguments) - func testIgnoringProbesInAsyncLet(argument: Argument) async throws { + func ignoringProbesInAsyncLet(argument: Argument) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithAsyncLet() @@ -130,7 +130,7 @@ extension ProbingOptionsTests { product(1 ..< 4, 1 ..< 3).map { "\($0)-\($1)" } @Test(arguments: ignoringEffectsInTasksArguments) - func testIgnoringEffectsInTask(withID id: EffectIdentifier) async throws { + func ignoringEffectsInTask(withID id: EffectIdentifier) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithTask() @@ -150,7 +150,7 @@ extension ProbingOptionsTests { } @Test(arguments: ignoringEffectsInTasksArguments) - func testIgnoringEffectsInTaskGroup(withID id: EffectIdentifier) async throws { + func ignoringEffectsInTaskGroup(withID id: EffectIdentifier) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithTaskGroup() @@ -170,7 +170,7 @@ extension ProbingOptionsTests { } @Test(arguments: ignoringEffectsInTasksArguments) - func testIgnoringEffectsInAsyncLet(withID id: EffectIdentifier) async throws { + func ignoringEffectsInAsyncLet(withID id: EffectIdentifier) async throws { try await withKnownIssue { try await withProbing(options: .ignoreProbingInTasks) { await interactor.callWithAsyncLet() @@ -258,7 +258,7 @@ extension ProbingOptionsTests { await #probe() model.tick() - #ConcurrentEffect("\(id)-2") { + #Effect("\(id)-2") { @concurrent in await #probe() await self.model.completeEffect(declaredID: "\(id)-2") return EffectIdentifier.current diff --git a/Tests/ProbeTestingTests/Suites/TaskGroupTests.swift b/Tests/ProbeTestingTests/Suites/TaskGroupTests.swift index 9bb8ffe..0d8db01 100644 --- a/Tests/ProbeTestingTests/Suites/TaskGroupTests.swift +++ b/Tests/ProbeTestingTests/Suites/TaskGroupTests.swift @@ -15,7 +15,7 @@ internal struct TaskGroupTests { private let childrenCount = 100 @Test - func testCollectingChildResults() async throws { + func collectingChildResults() async throws { try await withProbing { await withTaskGroup(of: Void.self) { group in for _ in 0 ..< childrenCount { diff --git a/Tests/ProbeTestingTests/Suites/WithProbingTests.swift b/Tests/ProbeTestingTests/Suites/WithProbingTests.swift index 312364d..c1274ef 100644 --- a/Tests/ProbeTestingTests/Suites/WithProbingTests.swift +++ b/Tests/ProbeTestingTests/Suites/WithProbingTests.swift @@ -22,7 +22,7 @@ internal struct WithProbingTests { } @Test - func testRunningWithoutDispatches() async throws { + func runningWithoutDispatches() async throws { try await withProbing { interactor.call() } dispatchedBy: { _ in @@ -32,7 +32,7 @@ internal struct WithProbingTests { } @Test - func testRunningUntilExitOfBody() async throws { + func runningUntilExitOfBody() async throws { try await withProbing { interactor.call() } dispatchedBy: { dispatcher in @@ -43,7 +43,7 @@ internal struct WithProbingTests { } @Test - func testRunningUntilEverythingCompleted() async throws { + func runningUntilEverythingCompleted() async throws { try await withProbing { interactor.call() } dispatchedBy: { dispatcher in @@ -54,7 +54,7 @@ internal struct WithProbingTests { } @Test - func testGettingMissingEffectValue() async throws { + func gettingMissingEffectValue() async throws { try await withKnownIssue { try await withProbing { interactor.call() @@ -74,7 +74,7 @@ internal struct WithProbingTests { } @Test - func testGettingMissingEffectCancelledValue() async throws { + func gettingMissingEffectCancelledValue() async throws { try await withKnownIssue { try await withProbing { interactor.call() @@ -94,7 +94,7 @@ internal struct WithProbingTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbe(options: ProbingOptions) async throws { + func runningUpToMissingProbe(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { interactor.call() @@ -113,7 +113,7 @@ internal struct WithProbingTests { } @Test(arguments: ProbingOptions.all) - func testRunningUpToMissingProbeInEffect(options: ProbingOptions) async throws { + func runningUpToMissingProbeInEffect(options: ProbingOptions) async throws { try await withKnownIssue { try await withProbing(options: options) { interactor.call() @@ -135,7 +135,7 @@ internal struct WithProbingTests { arguments: [true, false], ProbingOptions.all ) - func testRunningUntilMissingEffectCompleted( + func runningUntilMissingEffectCompleted( includingDescendants: Bool, options: ProbingOptions ) async throws { @@ -163,7 +163,7 @@ internal struct WithProbingTests { extension WithProbingTests { @Test - func testReturningValue() async throws { + func returningValue() async throws { let id = UUID() let value = try await withProbing { id @@ -174,7 +174,7 @@ extension WithProbingTests { } @Test - func testThrowingEarlyInBody() async { + func throwingEarlyInBody() async { await #expect(throws: ErrorMock.self) { try await withProbing { throw ErrorMock() @@ -186,7 +186,7 @@ extension WithProbingTests { } @Test - func testThrowingEarlyInTest() async { + func throwingEarlyInTest() async { await #expect(throws: ErrorMock.self) { try await confirmation { confirmation in try await withProbing { @@ -199,7 +199,7 @@ extension WithProbingTests { } @Test - func testProbingInTest() async throws { + func probingInTest() async throws { try await withProbing { // Void } dispatchedBy: { _ in @@ -209,7 +209,7 @@ extension WithProbingTests { @CustomActor @Test - func testIsolationInBody() async throws { + func isolationInBody() async throws { try await withProbing { #expect(#isolation === CustomActor.shared) CustomActor.shared.assertIsolated() @@ -220,7 +220,7 @@ extension WithProbingTests { @CustomActor @Test - func testIsolationInTest() async throws { + func isolationInTest() async throws { try await withProbing { // Void } dispatchedBy: { _ in diff --git a/Tests/ProbingMacrosTests/ConcurrentEffectMacroTests.swift b/Tests/ProbingMacrosTests/ConcurrentEffectMacroTests.swift deleted file mode 100644 index af0d646..0000000 --- a/Tests/ProbingMacrosTests/ConcurrentEffectMacroTests.swift +++ /dev/null @@ -1,163 +0,0 @@ -// -// ConcurrentEffectMacroTests.swift -// Probing -// -// Created by Kamil Strzelecki on 02/03/2025. -// Copyright © 2025 Kamil Strzelecki. All rights reserved. -// - -#if canImport(ProbingMacros) - import ProbingMacros - import SwiftSyntaxMacros - import SwiftSyntaxMacrosTestSupport - import XCTest - - internal final class ConcurrentEffectMacroTests: XCTestCase { - - private let macros: [String: any Macro.Type] = [ - "ConcurrentEffect": EffectMacro.self - ] - - func testExpansion() { - assertMacroExpansion( - #""" - #ConcurrentEffect("test") { - print("Hello") - } - """#, - expandedSource: - #""" - { - #if DEBUG - return TestableEffect._make( - "test", - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: { - print("Hello") - } - ) - #else - return Task( - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: { - print("Hello") - } - ) - #endif - }() - """#, - macros: macros - ) - } - - func testExpansionWithParameters() { - assertMacroExpansion( - #""" - #ConcurrentEffect( - "test", - preprocessorFlag: "UNIT_TESTS", - priority: .high, - operation: operation - ) - """#, - expandedSource: - #""" - { - #if UNIT_TESTS - return TestableEffect._make( - "test", - executorPreference: globalConcurrentExecutor, - priority: .high, - operation: operation - ) - #else - return Task( - executorPreference: globalConcurrentExecutor, - priority: .high, - operation: operation - ) - #endif - }() - """#, - macros: macros - ) - } - - func testExpansionWithNestedChildren() { - assertMacroExpansion( - #""" - #ConcurrentEffect("1") { - #Effect("2", priority: .high) { - print("Hello") - if true { - #ConcurrentEffect("3", operation: operation) - } else { - print("World") - } - } - print("!") - } - """#, - - expandedSource: - #""" - { - #if DEBUG - return TestableEffect._make( - "1", - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: { - TestableEffect._make( - "2", - priority: .high, - operation: { - print("Hello") - if true { - TestableEffect._make( - "3", - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: operation - ) - } else { - print("World") - } - } - ) - print("!") - } - ) - #else - return Task( - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: { - Task( - priority: .high, - operation: { - print("Hello") - if true { - Task( - executorPreference: globalConcurrentExecutor, - priority: nil, - operation: operation - ) - } else { - print("World") - } - } - ) - print("!") - } - ) - #endif - }() - """#, - macros: macros - ) - } - } -#endif diff --git a/Tests/DeeplyCopyableMacrosTests/DeeplyCopyableMacroTests.swift b/Tests/ProbingMacrosTests/DeeplyCopyable/DeeplyCopyableMacroTests.swift similarity index 98% rename from Tests/DeeplyCopyableMacrosTests/DeeplyCopyableMacroTests.swift rename to Tests/ProbingMacrosTests/DeeplyCopyable/DeeplyCopyableMacroTests.swift index e5e988b..d583d76 100644 --- a/Tests/DeeplyCopyableMacrosTests/DeeplyCopyableMacroTests.swift +++ b/Tests/ProbingMacrosTests/DeeplyCopyable/DeeplyCopyableMacroTests.swift @@ -6,8 +6,8 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -#if canImport(DeeplyCopyableMacros) - import DeeplyCopyableMacros +#if canImport(ProbingMacros) + import ProbingMacros import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest diff --git a/Tests/EquatableObjectMacrosTests/EquatableObjectMacroTests.swift b/Tests/ProbingMacrosTests/EquatableObject/EquatableObjectMacroTests.swift similarity index 96% rename from Tests/EquatableObjectMacrosTests/EquatableObjectMacroTests.swift rename to Tests/ProbingMacrosTests/EquatableObject/EquatableObjectMacroTests.swift index 0538322..81d0476 100644 --- a/Tests/EquatableObjectMacrosTests/EquatableObjectMacroTests.swift +++ b/Tests/ProbingMacrosTests/EquatableObject/EquatableObjectMacroTests.swift @@ -6,8 +6,8 @@ // Copyright © 2025 Kamil Strzelecki. All rights reserved. // -#if canImport(EquatableObjectMacros) - import EquatableObjectMacros +#if canImport(ProbingMacros) + import ProbingMacros import SwiftSyntaxMacros import SwiftSyntaxMacrosTestSupport import XCTest diff --git a/Tests/ProbingMacrosTests/EffectMacroTests.swift b/Tests/ProbingMacrosTests/Probing/EffectMacroTests.swift similarity index 72% rename from Tests/ProbingMacrosTests/EffectMacroTests.swift rename to Tests/ProbingMacrosTests/Probing/EffectMacroTests.swift index 844381a..951003f 100644 --- a/Tests/ProbingMacrosTests/EffectMacroTests.swift +++ b/Tests/ProbingMacrosTests/Probing/EffectMacroTests.swift @@ -18,7 +18,7 @@ "Effect": EffectMacro.self ] - func testExpansionWithIsolatedOperation() { + func testExpansion() { assertMacroExpansion( #""" #Effect("test") { @@ -38,6 +38,7 @@ ) #else return Task( + name: "test", priority: nil, operation: { print("Hello") @@ -50,7 +51,73 @@ ) } - func testExpansionWithIsolatedOperationAndParameters() { + func testExpansionWithGlobalActor() { + assertMacroExpansion( + #""" + #Effect("test") { @MainActor in + print("Hello") + } + """#, + expandedSource: + #""" + { + #if DEBUG + return TestableEffect._make( + "test", + priority: nil, + operation: { @MainActor in + print("Hello") + } + ) + #else + return Task( + name: "test", + priority: nil, + operation: { @MainActor in + print("Hello") + } + ) + #endif + }() + """#, + macros: macros + ) + } + + func testExpansionWithConcurrentAttribute() { + assertMacroExpansion( + #""" + #Effect("test") { @concurrent in + print("Hello") + } + """#, + expandedSource: + #""" + { + #if DEBUG + return TestableEffect._make( + "test", + priority: nil, + operation: { @concurrent in + print("Hello") + } + ) + #else + return Task( + name: "test", + priority: nil, + operation: { @concurrent in + print("Hello") + } + ) + #endif + }() + """#, + macros: macros + ) + } + + func testExpansionWithParameters() { assertMacroExpansion( #""" #Effect( @@ -71,6 +138,7 @@ ) #else return Task( + name: "test", priority: .high, operation: operation ) @@ -80,6 +148,9 @@ macros: macros ) } + } + + extension EffectMacroTests { func testExpansionWithExecutorPreference() { assertMacroExpansion( @@ -102,6 +173,7 @@ ) #else return Task( + name: "test", executorPreference: globalConcurrentExecutor, priority: nil, operation: { @@ -138,6 +210,7 @@ ) #else return Task( + name: "test", executorPreference: globalConcurrentExecutor, priority: .high, operation: operation @@ -148,12 +221,15 @@ macros: macros ) } + } + + extension EffectMacroTests { func testExpansionWithNestedChildren() { assertMacroExpansion( #""" #Effect("1") { - #ConcurrentEffect("2", priority: .high) { + #Effect("2", priority: .high) { @concurrent in print("Hello") if true { #Effect("3", operation: operation) @@ -175,9 +251,8 @@ operation: { TestableEffect._make( "2", - executorPreference: globalConcurrentExecutor, priority: .high, - operation: { + operation: { @concurrent in print("Hello") if true { TestableEffect._make( @@ -195,15 +270,17 @@ ) #else return Task( + name: "1", priority: nil, operation: { Task( - executorPreference: globalConcurrentExecutor, + name: "2", priority: .high, - operation: { + operation: { @concurrent in print("Hello") if true { Task( + name: "3", priority: nil, operation: operation ) diff --git a/Tests/ProbingMacrosTests/ProbeMacroTests.swift b/Tests/ProbingMacrosTests/Probing/ProbeMacroTests.swift similarity index 100% rename from Tests/ProbingMacrosTests/ProbeMacroTests.swift rename to Tests/ProbingMacrosTests/Probing/ProbeMacroTests.swift diff --git a/Tests/ProbingTests/EffectNameTests.swift b/Tests/ProbingTests/EffectNameTests.swift index ec09939..3d2d987 100644 --- a/Tests/ProbingTests/EffectNameTests.swift +++ b/Tests/ProbingTests/EffectNameTests.swift @@ -12,7 +12,7 @@ import Testing internal struct EffectNameTests { @Test - func testEquality() { + func equality() { let name: EffectName = "effect" let enumeratedName = EffectName.enumerated(name) @@ -21,7 +21,7 @@ internal struct EffectNameTests { } @Test - func testIndexing() { + func indexing() { let name: EffectName = "effect" let enumeratedName = EffectName.enumerated(name) diff --git a/Tests/ProbingTests/EffectTests.swift b/Tests/ProbingTests/EffectTests.swift index 21506cf..0c8b470 100644 --- a/Tests/ProbingTests/EffectTests.swift +++ b/Tests/ProbingTests/EffectTests.swift @@ -9,12 +9,12 @@ @testable import Probing import Testing -internal struct EffectTests { +internal enum EffectTests { struct WithIsolatedOperation { @Test - func testTestableEffectInit() async { + func effectInit() async { await confirmation { confirmation in let effect = #Effect("Test") { try? await Task.sleep(for: .microseconds(1)) @@ -28,10 +28,10 @@ internal struct EffectTests { } @Test - func testTaskInit() async { + func taskInit() async { await confirmation { confirmation in let effect = #Effect( - "Test", + EffectName(rawValue: "Test"), preprocessorFlag: "NULL", operation: { try? await Task.sleep(for: .microseconds(1)) @@ -47,9 +47,10 @@ internal struct EffectTests { @CustomActor @Test - func testIsolation() async { + func isolation() async { let effect = #Effect("Test") { - #expect(#isolation === CustomActor.shared) + let isolation = #isolation + #expect(isolation === CustomActor.shared) CustomActor.shared.assertIsolated() } await effect.value @@ -59,7 +60,7 @@ internal struct EffectTests { struct WithExecutorPreference { @Test - func testTestableEffectInit() async { + func effectInit() async { await confirmation { confirmation in let effect = #Effect("Test", executorPreference: globalConcurrentExecutor) { try? await Task.sleep(for: .microseconds(1)) @@ -73,10 +74,10 @@ internal struct EffectTests { } @Test - func testTaskInit() async { + func taskInit() async { await confirmation { confirmation in let effect = #Effect( - "Test", + EffectName(rawValue: "Test"), preprocessorFlag: "NULL", executorPreference: globalConcurrentExecutor, operation: { @@ -93,9 +94,10 @@ internal struct EffectTests { @CustomActor @Test - func testIsolation() async { + func isolation() async { let effect = #Effect("Test", executorPreference: globalConcurrentExecutor) { - #expect(#isolation == nil) + let isolation = #isolation + #expect(isolation == nil) } await effect.value } @@ -104,9 +106,9 @@ internal struct EffectTests { struct Concurrent { @Test - func testTestableEffectInit() async { + func effectInit() async { await confirmation { confirmation in - let effect = #ConcurrentEffect("Test") { + let effect = #Effect("Test") { @concurrent in try? await Task.sleep(for: .microseconds(1)) confirmation() } @@ -118,12 +120,12 @@ internal struct EffectTests { } @Test - func testTaskInit() async { + func taskInit() async { await confirmation { confirmation in - let effect = #ConcurrentEffect( - "Test", + let effect = #Effect( + EffectName(rawValue: "Test"), preprocessorFlag: "NULL", - operation: { + operation: { @concurrent in try? await Task.sleep(for: .microseconds(1)) confirmation() } @@ -137,9 +139,10 @@ internal struct EffectTests { @CustomActor @Test - func testIsolation() async { - let effect = #ConcurrentEffect("Test") { - #expect(#isolation == nil) + func isolation() async { + let effect = #Effect("Test") { @concurrent in + let isolation = #isolation + #expect(isolation == nil) } await effect.value } @@ -148,11 +151,11 @@ internal struct EffectTests { struct Recursive { @Test - func testTestableEffectNestedChildrenInit() async { + func effectNestedChildrenInit() async { await confirmation { confirmation in let effect = #Effect("1") { #Effect("2", executorPreference: globalConcurrentExecutor) { - #ConcurrentEffect("3", priority: .high) { + #Effect("3", priority: .high) { @concurrent in confirmation() } } @@ -165,11 +168,11 @@ internal struct EffectTests { } @Test - func testTaskNestedChildrenInit() async { + func taskNestedChildrenInit() async { await confirmation { confirmation in let effect = #Effect("1", preprocessorFlag: "NULL") { #Effect("2", executorPreference: globalConcurrentExecutor) { - #ConcurrentEffect("3", priority: .high) { + #Effect("3", priority: .high) { @concurrent in confirmation() } } diff --git a/Tests/ProbingTests/ProbeTests.swift b/Tests/ProbingTests/ProbeTests.swift index 7cc6f2a..b8f6b86 100644 --- a/Tests/ProbingTests/ProbeTests.swift +++ b/Tests/ProbingTests/ProbeTests.swift @@ -12,12 +12,12 @@ import Testing internal struct ProbeTests { @Test - func testProbe() async { + func asyncFunction() async { await #probe() } @Test - func testProbeInTask() async { + func task() async { let task = Task { await #probe() } @@ -25,7 +25,7 @@ internal struct ProbeTests { } @Test - func testProbeInExplicitlyIsolatedTask() async { + func explicitlyIsolatedTask() async { let task = Task { @CustomActor in await #probe() } @@ -33,7 +33,7 @@ internal struct ProbeTests { } @Test - func testProbeInEffect() async { + func effect() async { let effect = #Effect("test") { await #probe() } @@ -41,7 +41,7 @@ internal struct ProbeTests { } @Test - func testProbeInExplicitlyIsolatedEffect() async { + func explicitlyIsolatedEffect() async { let effect = #Effect("test") { @CustomActor in await #probe() }