Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 12 additions & 29 deletions .github/workflows/run_tests.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: Build And Test PredicateBuilder
permissions:
contents: read

on:
push:
Expand All @@ -9,40 +11,21 @@ concurrency:
cancel-in-progress: true
jobs:
predicate-builder-package-tests:
name: XCTest | Xcode ${{ matrix.xcode-version }} on ${{ matrix.os }}
strategy:
matrix:
os: [macos-13]
xcode-version: ["14.2", "14.3"]
env:
DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer"
runs-on: ${{ matrix.os }}
name: XCTest | macOS Latest
runs-on: macos-latest
defaults:
run:
working-directory: ./PredicateBuilder
working-directory: ./
steps:
- uses: actions/checkout@v3
- name: Build Swift Package
run: swift build
- name: Run Tests
run: swift test
- name: Type Inference Tests | Xcode ${{ matrix.xcode-version }} on ${{ matrix.os }}
working-directory: ./PredicateBuilder/CompilationTests/Tests/
run: ./test-compilation.sh
# predicate-builder-macro-package-tests:
# name: Test PredicateBuilderMacro Package | Xcode ${{ matrix.xcode-version }} on ${{ matrix.os }}
# runs-on: macos-13
# strategy:
# matrix:
# xcode-version: ["15.0.0"]
# env:
# DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer"
# defaults:
# run:
# working-directory: ./PredicateBuilderMacro
# steps:
# - uses: actions/checkout@v3
# - name: Build Swift Package
# run: swift build
# - name: Run Tests
# run: swift test
- name: Public API Consumption Tests
working-directory: ./ConsumptionTest
run: |
echo "Testing valid code compiles..."
swift build --target ValidCode
echo "Testing invalid code fails to compile..."
./Tests/test-compilation.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
14 changes: 14 additions & 0 deletions ConsumptionTest/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions ConsumptionTest/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "ConsumptionTest",
platforms: [
.iOS(.v16),
.macOS(.v12),
],
dependencies: [
.package(path: "..")
],
targets: [
.executableTarget(
name: "ValidCode",
dependencies: [
.product(name: "PredicateBuilder", package: "predicate-builder"),
],
path: "Sources/ValidCode"
),
.executableTarget(
name: "InvalidCode",
dependencies: [
.product(name: "PredicateBuilder", package: "predicate-builder"),
],
path: "Sources/InvalidCode"
),
]
)
28 changes: 28 additions & 0 deletions ConsumptionTest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# ConsumptionTest

This package tests that PredicateBuilder can be consumed correctly through its public API.

## Purpose

This test verifies both positive and negative compilation cases:

**Positive Tests (ValidCode target):**
- Users only need to `import PredicateBuilder` (single import)
- The macro (`#PredicateBuilder`) is automatically available
- The result builder (`@PredicateBuilder`) works correctly
- All types and functionality are accessible through the single import

**Negative Tests (InvalidCode target):**
- Type checking works correctly - invalid code fails to compile
- Compiler errors are appropriate and prevent runtime crashes
- Type constraints are enforced through the public API

## Why This Exists

The main test suite uses `@testable import`, which bypasses public API boundaries and allows importing internal modules directly. This test uses only the public API, ensuring that:

1. The package structure is correct
2. Re-exports work as intended
3. The public API contract is maintained
4. Breaking changes to the public API are caught
5. Type checking works correctly through the public API
20 changes: 20 additions & 0 deletions ConsumptionTest/Sources/InvalidCode/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import PredicateBuilder

// MARK: - Invalid Code Tests
// These should NOT compile - they test type checking behavior
// This file is intentionally invalid and should cause compilation to fail

@main
struct InvalidCode {
static func main() {
@PredicateBuilder<NSString> var valid: NSPredicate {
\NSString.boolValue == true
}

@PredicateBuilder<NSInteger> var shouldNotCompile: NSPredicate {
\NSString.boolValue == true // Wrong type - should fail compilation
}
}
}

25 changes: 25 additions & 0 deletions ConsumptionTest/Sources/ValidCode/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import CoreData
import PredicateBuilder

// MARK: - Valid Code Tests
// These should compile successfully when using the public API

class Test: NSManagedObject {
@NSManaged var name: String
}

@main
struct ValidCode {
static func main() {
// Test macro usage - if this compiles, the macro is available via public API
let _: NSPredicate = #PredicateBuilder<Test> {
\.name == "hello"
}

// Test result builder usage - if this compiles, the builder is available via public API
@PredicateBuilder<Test>
var _unused: NSPredicate {
\.name == "hello"
}
}
}
28 changes: 28 additions & 0 deletions ConsumptionTest/Tests/test-compilation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

# Test that invalid code fails to compile with the expected error
# This ensures type checking is working correctly through the public API

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR/.."

echo "Testing that invalid code fails to compile..."
BUILD_OUTPUT=$(swift build --target InvalidCode 2>&1)
BUILD_EXIT_CODE=$?

echo -e "Build output:\n $BUILD_OUTPUT"

if [ $BUILD_EXIT_CODE -eq 0 ]; then
echo "ERROR: Invalid code compiled successfully! Type checking may not be working correctly."
exit 1
fi

if [[ $BUILD_OUTPUT != *"'PredicateBuilder' requires that 'NSString' inherit from 'NSManagedObject'"* ]]; then
echo "ERROR: Compilation failed, but for the wrong reason!"
echo "Expected error message about 'NSString' inheriting from 'NSManagedObject'"
echo "Make sure the compiler is not inferring the root type to be 'NSManagedObject'"
exit 1
fi

echo "✅ The compiler failed in the expected manner. The PredicateBuilder's type checking is working correctly through the public API."

14 changes: 14 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 66 additions & 38 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// swift-tools-version: 5.7
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
import CompilerPluginSupport

let package = Package(
name: "PredicateBuilder",
Expand All @@ -20,60 +21,87 @@ let package = Package(
)
],
dependencies: [
.package(path: "PredicateBuilderCore"),
.package(path: "PredicateBuilderTestData")
].withMacroDependencyIfPossible(),
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.1")
],
targets: [
.target(
name: "PredicateBuilderCore",
path: "PredicateBuilderCore/Sources/PredicateBuilderCore"
),
.target(
name: "PredicateBuilder",
dependencies: [
.product(name: "PredicateBuilderCore", package: "PredicateBuilderCore")
].withMacroDependencyIfPossible(),
.target(name: "PredicateBuilderCore"),
.target(name: "PredicateBuilderMacro")
],
path: "PredicateBuilder/Sources"
),
.macro(
name: "PredicateBuilderMacroMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
.target(name: "PredicateBuilderCore")
],
path: "PredicateBuilderMacro/Sources/PredicateBuilderMacroMacros"
),
.target(
name: "PredicateBuilderMacro",
dependencies: [
.target(name: "PredicateBuilderCore"),
"PredicateBuilderMacroMacros"
],
path: "PredicateBuilderMacro/Sources/PredicateBuilderMacro"
),
.target(
name: "PredicateBuilderTestData",
path: "PredicateBuilderTestData/Sources/PredicateBuilderTestData"
),
.executableTarget(
name: "PredicateBuilderExample",
dependencies: [
.target(name: "PredicateBuilder"),
.product(name: "PredicateBuilderTestData", package: "PredicateBuilderTestData")
].withMacroDependencyIfPossible(),
.target(name: "PredicateBuilderTestData")
],
path: "PredicateBuilderExample"
),
.executableTarget(
name: "PredicateBuilderMacroClient",
dependencies: [
.target(name: "PredicateBuilderMacro"),
.target(name: "PredicateBuilderTestData")
],
path: "PredicateBuilderMacro/PredicateBuilderMacroClient"
),
.testTarget(
name: "PredicateBuilderTests",
dependencies: [
.target(name: "PredicateBuilder"),
.product(name: "PredicateBuilderTestData", package: "PredicateBuilderTestData")
].withMacroDependencyIfPossible(),
.target(name: "PredicateBuilderTestData")
],
path: "PredicateBuilder/Tests"
),
.testTarget(
name: "PredicateBuilderCoreTests",
dependencies: [
.target(name: "PredicateBuilderCore")
],
path: "PredicateBuilderCore/Tests/PredicateBuilderCoreTests"
),
.testTarget(
name: "PredicateBuilderMacroTests",
dependencies: [
"PredicateBuilderMacroMacros",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
],
path: "PredicateBuilderMacro/Tests/PredicateBuilderMacroTests"
),
.testTarget(
name: "PredicateBuilderTestDataTests",
dependencies: [
.target(name: "PredicateBuilderTestData")
],
path: "PredicateBuilderTestData/Tests/PredicateBuilderTestDataTests"
),
]
)

extension Array where Element == Target.Dependency {
func withMacroDependencyIfPossible() -> Self {
#if(swift(<5.9))
return self
#else
var array = self
array.append(
.product(name: "PredicateBuilderMacro", package: "PredicateBuilderMacro")
)
return array
#endif
}
}

extension Array where Element == Package.Dependency {
func withMacroDependencyIfPossible() -> Self {
#if(swift(<5.9))
return self
#else
var array = self
array.append(
.package(path: "PredicateBuilderMacro")
)
return array
#endif
}
}
23 changes: 0 additions & 23 deletions PredicateBuilder/CompilationTests/Package.swift

This file was deleted.

Loading
Loading