Skip to content

Commit e63d7f8

Browse files
committed
Update README and remove compound module concept
I deemed the compound module concept as useless for now. If we want to import protocol with an identical name from different targets, we should work on prefixing them with their target name in the generated mocks.
1 parent 5f33967 commit e63d7f8

File tree

3 files changed

+26
-77
lines changed

3 files changed

+26
-77
lines changed

Generator/Plugin/Modular/CuckooPluginModular.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ struct CuckooPluginModular: BuildToolPlugin {
3737
}
3838

3939
return try commandsPerModule(
40-
targetName: target.name,
4140
sourceModules: allSourceModules,
4241
executableFactory: context.tool(named:),
4342
projectDir: context.package.directoryURL,
@@ -86,7 +85,6 @@ extension CuckooPluginModular: XcodeBuildToolPlugin {
8685
allSourceModules.append(selfModule)
8786

8887
return try commandsPerModule(
89-
targetName: target.displayName,
9088
sourceModules: allSourceModules,
9189
executableFactory: context.tool(named:),
9290
projectDir: context.xcodeProject.directoryURL,
@@ -102,7 +100,6 @@ struct SourceModule {
102100
}
103101

104102
private func commandsPerModule(
105-
targetName: String,
106103
sourceModules: [SourceModule],
107104
executableFactory: (String) throws -> PluginContext.Tool,
108105
projectDir: URL,
@@ -123,7 +120,6 @@ private func commandsPerModule(
123120
"DERIVED_SOURCES_DIR": derivedSourcesDir.path(),
124121
"CUCKOO_OVERRIDE_OUTPUT": outputURL.path(),
125122
"CUCKOO_MODULE_NAME": sourceModule.name,
126-
"CUCKOO_COMPOUND_MODULE_NAME": "\(targetName)/\(sourceModule.name)",
127123
],
128124
inputFiles: [configurationURL] + sourceModule.sources,
129125
outputFiles: [outputURL]

Generator/Sources/CLI/GenerateCommand.swift

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ struct GenerateCommand: AsyncParsableCommand {
5656
)
5757

5858
if modules.isEmpty {
59-
let compoundModuleName = ProcessInfo.processInfo.environment["CUCKOO_COMPOUND_MODULE_NAME"]
6059
let requestedModuleName = ProcessInfo.processInfo.environment["CUCKOO_MODULE_NAME"]
61-
let effectiveName = compoundModuleName ?? requestedModuleName
62-
if let effectiveName {
63-
log(.info, message: "No module named '\(effectiveName)' found in Cuckoofile, skipping generation.")
60+
if let requestedModuleName {
61+
log(.info, message: "No module named '\(requestedModuleName)' found in Cuckoofile, skipping generation.")
6462
}
6563
if let outputPath = overriddenOutput {
6664
let path = Path(outputPath, expandingTilde: true)
@@ -124,7 +122,6 @@ struct GenerateCommand: AsyncParsableCommand {
124122

125123
func modules(configurationPath: Path, contents: String) throws -> [Module] {
126124
let requestedModuleName = ProcessInfo.processInfo.environment["CUCKOO_MODULE_NAME"]
127-
let compoundModuleName = ProcessInfo.processInfo.environment["CUCKOO_COMPOUND_MODULE_NAME"]
128125

129126
var errorMessages: [String] = []
130127
var globalOutput: String? = overriddenOutput
@@ -188,39 +185,9 @@ struct GenerateCommand: AsyncParsableCommand {
188185
throw GenerateError.configurationErrors(details: errorMessages)
189186
}
190187

191-
let filteredModules = filterModulesByEnvironment(
192-
allModules: allModules,
193-
compoundModuleName: compoundModuleName,
194-
requestedModuleName: requestedModuleName
195-
)
196-
197-
return filteredModules
198-
}
199-
200-
/// Filter modules based on environment variables set by the plugin.
201-
/// Priority: compound module name (TARGET/MODULE) > plain module name > all modules.
202-
/// This allows test targets to override shared dependency mock generation.
203-
private func filterModulesByEnvironment(
204-
allModules: [Module],
205-
compoundModuleName: String?,
206-
requestedModuleName: String?
207-
) -> [Module] {
208-
if let compoundModuleName {
209-
let compoundMatches = allModules.filter { $0.name == compoundModuleName }
210-
if !compoundMatches.isEmpty {
211-
// Compound key (TARGET/MODULE) found – use it exclusively.
212-
// An entry with empty sources acts as a suppressor, producing an empty output file.
213-
return compoundMatches
214-
} else if let requestedModuleName {
215-
// No compound override – fall back to the plain module name.
216-
return allModules.filter { $0.name == requestedModuleName }
217-
} else {
218-
return []
219-
}
220-
} else if let requestedModuleName {
188+
if let requestedModuleName {
221189
return allModules.filter { $0.name == requestedModuleName }
222190
}
223-
224191
return allModules
225192
}
226193

README.md

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -133,63 +133,49 @@ target = "Cuckoonator"
133133

134134
#### For CuckooPluginModular
135135

136-
When using `CuckooPluginModular`, the plugin automatically detects source modules from your test target's dependencies and generates a dedicated mock file for each one (`GeneratedMocks_<ModuleName>.swift`). It supports compound module names (`TARGET/MODULE`) in `Cuckoofile.toml` so that different test targets can customize mock generation for shared dependencies.
136+
`CuckooPluginSingleFile` puts all mocks into a single `GeneratedMocks.swift` file in derived data. In a Swift Package with multiple targets this is problematic because each test target compiles independently and may not have visibility into types from unrelated modules. Use `CuckooPluginModular` instead: it inspects your test target's dependencies and runs the generator once per dependency module, producing a separate `GeneratedMocks_<ModuleName>.swift` for each.
137+
138+
A `Cuckoofile.toml` entry is required for each module you want to generate mocks for. The plugin looks up the test target's own name (e.g. `[modules.TargetATests]`), letting you specify which source files to mock and which imports to add. Modules without a matching entry produce an empty file and no mocks.
137139

138140
**Example `Cuckoofile.toml` for modular projects:**
139141

140142
```toml
141-
# CoreModule mocks
142-
[modules.CoreModuleTests]
143-
imports = ["Foundation"]
144-
testableImports = ["CoreModule"]
145-
sources = [
146-
"Sources/CoreModule/ServiceProtocol.swift"
147-
]
148-
149-
# NetworkAPI module mocks
150-
[modules.FirstNetworkAPITests]
143+
# TargetA mocks
144+
[modules.TargetATests]
151145
imports = ["Foundation"]
152-
testableImports = ["FirstNetworkAPI"]
146+
testableImports = ["TargetA"] #ProtocolA is internal to TargetA
153147
sources = [
154-
"Sources/FirstNetworkAPI/Generated/Client.swift"
148+
"Sources/TargetA/InternalProtocolA.swift"
155149
]
156150

157-
# SecondNetworkAPI module mocks
158-
[modules.SecondNetworkAPITests]
159-
imports = ["Foundation"]
160-
testableImports = ["SecondNetworkAPI"]
151+
# AggregationTarget mocks - demonstrates multiple testableImports
152+
[modules.AggregationTargetTests]
153+
imports = ["Foundation", "TargetA", "TargetB"]
161154
sources = [
162-
"Sources/SecondNetworkAPI/Generated/Client.swift"
163-
]
164-
165-
# FeatureModule mocks - demonstrates multiple testableImports
166-
[modules.FeatureModuleTests]
167-
imports = ["Foundation"]
168-
testableImports = ["FirstNetworkAPI", "SecondNetworkAPI"]
169-
sources = [
170-
"Sources/FirstNetworkAPI/FirstNetworkAPI.swift",
171-
"Sources/SecondNetworkAPI/SecondNetworkAPI.swift"
155+
"Sources/TargetA/ProtocolA.swift",
156+
"Sources/TargetB/ProtocolB.swift",
172157
]
173158
```
174159

175-
**Note:** The modules specified in `testableImports` must be added as dependencies of your test target in `Package.swift`. For example:
160+
`Package.swift`:
176161

177162
```swift
178163
.testTarget(
179-
name: "CoreModuleTests",
180-
dependencies: ["CoreModule", "Cuckoo"]
164+
name: "TargetATests",
165+
dependencies: ["TargetA", "Cuckoo"],
166+
plugins: [
167+
.plugin(name: "CuckooPluginModular", package: "Cuckoo"),
168+
]
181169
),
182170
.testTarget(
183-
name: "FeatureModuleTests",
184-
dependencies: ["FirstNetworkAPI", "SecondNetworkAPI", "Cuckoo"]
171+
name: "AggregationTargetTests",
172+
dependencies: ["AggregationTarget", "Cuckoo"],
173+
plugins: [
174+
.plugin(name: "CuckooPluginModular", package: "Cuckoo"),
175+
]
185176
)
186177
```
187178

188-
This approach is essential when:
189-
- **Multiple modules define protocols/types with identical names** (e.g., both `ModuleA` and `ModuleB` have a `ServiceProtocol`)
190-
- Different test targets need different subsets of mocks from the same module
191-
- You want to organize and namespace mocks by test target for clarity
192-
193179
### 3. Usage
194180
Usage of Cuckoo is similar to [Mockito](http://mockito.org/) and [Hamcrest](http://hamcrest.org/). However, there are some differences and limitations caused by generating the mocks and Swift language itself. List of all the supported features can be found below. You can find complete examples in [tests](Tests).
195181

0 commit comments

Comments
 (0)