Skip to content

[Incremental] Add a check of the result of running the built app to the IncrementalTestFramework #540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ class AddFuncInImportedExtensionTest: XCTestCase {
func cu() {_ = C()}
""")

let mainFile = Source(named: "main", containing: "")

let mainModule = Module(
named: "main",
containing: [structConstructor, classConstructor],
containing: [mainFile, structConstructor, classConstructor],
importing: [importedModule],
producing: .executable)

Expand All @@ -69,14 +71,14 @@ class AddFuncInImportedExtensionTest: XCTestCase {
// Define what is expected
let whenAddOrRmFunc = ExpectedCompilations(
always: [structExtension, classExtension, ],
andWhenDisabled: [structConstructor, classConstructor])
andWhenDisabled: [mainFile, structConstructor, classConstructor])

let steps = [
Step( compiling: modules),
Step( compiling: modules, expecting: .none),
Step(adding: "withFunc", compiling: modules, expecting: whenAddOrRmFunc),
Step( compiling: modules, expecting: whenAddOrRmFunc),
Step(adding: "withFunc", compiling: modules, expecting: whenAddOrRmFunc),
Step( building: modules, .expecting(modules.allSourcesToCompile)),
Step( building: modules, .expecting(.none)),
Step(adding: "withFunc", building: modules, .expecting(whenAddOrRmFunc)),
Step( building: modules, .expecting(whenAddOrRmFunc)),
Step(adding: "withFunc", building: modules, .expecting(whenAddOrRmFunc)),
]

// Do the test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,54 @@ import SwiftOptions
import IncrementalTestFramework

/// Add and remove function in imported struct and in imported extension of imported struct.
/// This is a very complicated test, so it is built programmatically.
class HideAndShowFuncInStructAndExtensionTests: XCTestCase {
func testHideAndShowFuncInStructAndExtension() throws {
try IncrementalTest.perform(steps)
}
}

/// The changes to be tested
fileprivate let stepChanges: [[Change]] = [
[],
[],
[.exposeFuncInStruct],
[],
[.exposeFuncInExtension],
[],
Change.allCases,
[],
[.exposeFuncInStruct],
[.exposeFuncInExtension],
[.exposeFuncInStruct],
Change.allCases,
[.exposeFuncInExtension],
Change.allCases
]


// MARK: - Reify the source changes

fileprivate enum Change: String, CustomStringConvertible, CaseIterable {
/// Make the imported specific method defined in a structure public
case exposeFuncInStruct

/// Make the imported specific method defined in an extension public
case exposeFuncInExtension

var name: String {rawValue}
var description: String {name}
}

// MARK: - Define imported module
let imported = Source(named: "imported", containing: """
// MARK: - Define imported module
fileprivate let imported = Source(named: "imported", containing: """
// Just for fun, a protocol, as well as the struc with the optional specific func.
public protocol PP {}
public struct S: PP {
public init() {}
// Optionally expose a specific function in the structure
//# publicInStruct public
static func inStruct(_ i: Int) {print("1: private")}
//# \(Change.exposeFuncInStruct) public
static func inStruct(_ i: Int) {print("specific in struct")}
func fo() {}
}
public struct T {
Expand All @@ -38,93 +74,125 @@ class HideAndShowFuncInStructAndExtensionTests: XCTestCase {
}
extension S {
// Optionally expose a specific function in the extension
//# publicInExtension public
static func inExtension(_ i: Int) {print("2: private")}
//# \(Change.exposeFuncInExtension) public
static func inExtension(_ i: Int) {print("specific in extension")}
}
""")

let importedModule = Module(named: "importedModule",
containing: [imported],
producing: .library)
fileprivate let importedModule = Module(named: "importedModule",
containing: [imported],
producing: .library)

// MARK: - Define the main module
// MARK: - Define the main module

let instantiatesS = Source(named: "instantiatesS", containing: """
fileprivate let instantiatesS = Source(named: "instantiatesS", containing: """
// Instantiate S
import \(importedModule.name)
func late() { _ = S() }
""")

let callFunctionInExtension = Source(named: "callFunctionInExtension", containing: """
fileprivate let callFunctionInExtension = Source(named: "callFunctionInExtension", containing: """
// Call the function defined in an extension
import \(importedModule.name)
func fred() { S.inExtension(3) }
""")

let noUseOfS = Source(named: "noUseOfS", containing: """
fileprivate let noUseOfS = Source(named: "noUseOfS", containing: """
/// Call a function in an unchanging struct
import \(importedModule.name)
func baz() { T.bar("asdf") }
""")

let main = Source(named: "main", containing: """
fileprivate let main = Source(named: "main", containing: """
/// Extend S with general functions
import \(importedModule.name)
extension S {
static func inStruct<I: SignedInteger>(_ si: I) {
print("1: not public")
print("general in struct")
}
static func inExtension<I: SignedInteger>(_ si: I) {
print("2: not public")
print("general in extension")
}
}
S.inStruct(3)
S.inExtension(4)
""")

let mainModule = Module(named: "mainModule",
containing: [instantiatesS,
callFunctionInExtension,
noUseOfS,
main],
importing: [importedModule],
producing: .executable)

// MARK: - Define the test

let modules = [importedModule, mainModule]

let addOrRmInStruct = ExpectedCompilations(
always: [callFunctionInExtension, imported, instantiatesS, main],
andWhenDisabled: [noUseOfS])

// Interestingly, changes to the imported extension do not change the
/// structure's instantiation. (Compare to above.)
let addOrRmInExt = ExpectedCompilations(
always: [callFunctionInExtension, imported, main],
andWhenDisabled: [instantiatesS, noUseOfS])

let addOrRmBoth = addOrRmInStruct

let steps = [
Step( compiling: modules),
Step(adding: "publicInStruct" , compiling: modules, expecting: addOrRmInStruct),
Step( compiling: modules, expecting: addOrRmInStruct),
Step(adding: "publicInExtension" , compiling: modules, expecting: addOrRmInExt ),
Step( compiling: modules, expecting: addOrRmInExt ),
Step(adding: "publicInStruct", "publicInExtension", compiling: modules, expecting: addOrRmBoth ),
Step( compiling: modules, expecting: addOrRmBoth ),
Step(adding: "publicInStruct" , compiling: modules, expecting: addOrRmInStruct),
Step(adding: "publicInExtension", compiling: modules, expecting: addOrRmInStruct),
Step(adding: "publicInStruct" , compiling: modules, expecting: addOrRmInStruct),
Step(adding: "publicInStruct", "publicInExtension", compiling: modules, expecting: addOrRmInExt ),
Step(adding: "publicInExtension", compiling: modules, expecting: addOrRmInStruct),
Step(adding: "publicInStruct", "publicInExtension", compiling: modules, expecting: addOrRmInStruct),
]
fileprivate let mainModule = Module(named: "mainModule",
containing: [instantiatesS,
callFunctionInExtension,
noUseOfS,
main],
importing: [importedModule],
producing: .executable)

// MARK: - Define the whole app
fileprivate let modules = [importedModule, mainModule]

// MARK: - Compute the expectations
fileprivate extension Change {
var expectedCompilationsWithIncrementalImports: [Source] {
switch self {
case .exposeFuncInStruct: return [callFunctionInExtension, imported, instantiatesS, main]
case .exposeFuncInExtension: return [callFunctionInExtension, imported, main]
}
}

try IncrementalTest.perform(steps)
var locusOfExposure: String {
switch self {
case .exposeFuncInStruct: return "struct"
case .exposeFuncInExtension: return "extension"
}
}
static var allLociOfExposure: [String] {
allCases.map {$0.locusOfExposure}
}
}

// MARK: - Building a step from combinations of Changes
fileprivate extension Array where Element == Change {
var addOns: [String] {map {$0.name} }

func expectedCompilations(_ prevStep: Step?) -> ExpectedCompilations {
guard let prevStep = prevStep else {
return modules.allSourcesToCompile
}
let deltas = Set(map{$0.name}).symmetricDifference(prevStep.addOns.map{$0.name})
.map {Change.init(rawValue: $0)!}

let expectedCompilationsWithIncrementalImports: [Source] =
deltas.reduce(into: Set<Source>()) {sources, change in
sources.formUnion(change.expectedCompilationsWithIncrementalImports)
}
.sorted()

let andWhenDisabled = deltas.isEmpty
? []
: Set(modules.allSources).subtracting(expectedCompilationsWithIncrementalImports).sorted()

return ExpectedCompilations(always: expectedCompilationsWithIncrementalImports, andWhenDisabled: andWhenDisabled)
}

var expectedOutput: ExpectedProcessResult {
let specOrGen = Change.allCases .map {
contains($0) ? "specific" : "general"
}
let output = zip(specOrGen, Change.allLociOfExposure)
.map { "\($0.0) in \($0.1)"}
.joined(separator: "\n")
return ExpectedProcessResult(output: output)
}

func step(prior: Step?) -> Step {
Step(adding: addOns,
building: modules,
.expecting(expectedCompilations(prior), expectedOutput))
}
}
// MARK: - All steps

var steps: [Step] {
stepChanges.reduce(into: []) { steps, changes in
steps.append(changes.step(prior: steps.last))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ class RenameMemberOfImportedStructTest: XCTestCase {
andWhenDisabled: [other])

let steps = [
Step(adding: ["original"], compiling: modules),
Step(adding: ["original"], compiling: modules, expecting: .none),
Step(adding: ["renamed"], compiling: modules, expecting: whenRenaming),
Step(adding: ["original"], compiling: modules, expecting: whenRenaming),
Step(adding: ["renamed"], compiling: modules, expecting: whenRenaming),
Step(adding: ["original"], building: modules, .expecting(modules.allSourcesToCompile)),
Step(adding: ["original"], building: modules, .expecting(.none)),
Step(adding: ["renamed"], building: modules, .expecting(whenRenaming)),
Step(adding: ["original"], building: modules, .expecting(whenRenaming)),
Step(adding: ["renamed"], building: modules, .expecting(whenRenaming)),
]
try IncrementalTest.perform(steps)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class SpecificFuncAdditionInExtensionWithinModuleTest: XCTestCase {
// MARK: - Define the module
let main = Source(named: "main", containing: """
// Define a struct with a general method and call it
struct S {static func foo<I: SignedInteger>(_ si: I) {}}
struct S {static func foo<I: SignedInteger>(_ si: I) {print("general")}}
S.foo(3)
""")
let sExtension = Source(named: "sExtension", containing: """
// Extend the structure and optionally add a specific method
extension S {
//# withFunc static func foo(_ i: Int) {}
//# specificFuncInExtension static func foo(_ i: Int) {print("specific")}
}
// Also define a structure that won't be changed.
struct T {static func foo() {}}
Expand All @@ -43,18 +43,20 @@ class SpecificFuncAdditionInExtensionWithinModuleTest: XCTestCase {
func bar() {_ = S()}
""")

let mainModule = Module(named: "mainM",
let mainModule = Module(named: "mainM",
containing: [main, sExtension, userOfT, instantiator],
producing: .executable)


let whenAddOrRmSpecificFunc = ExpectedCompilations(always: [main, sExtension],
andWhenDisabled: [])

let steps = [
Step( compiling: [mainModule]),
Step( compiling: [mainModule], expecting: .none),
Step(adding: "withFunc", compiling: [mainModule], expecting: whenAddOrRmSpecificFunc),
Step( compiling: [mainModule], expecting: whenAddOrRmSpecificFunc),
Step(adding: "withFunc", compiling: [mainModule], expecting: whenAddOrRmSpecificFunc),
Step( building: [mainModule], .expecting([mainModule].allSourcesToCompile, "general")),
Step( building: [mainModule], .expecting(.none, "general")),
Step(adding: "specificFuncInExtension", building: [mainModule], .expecting(whenAddOrRmSpecificFunc, "specific")),
Step( building: [mainModule], .expecting(whenAddOrRmSpecificFunc, "general")),
Step(adding: "specificFuncInExtension", building: [mainModule], .expecting(whenAddOrRmSpecificFunc, "specific")),
]

try IncrementalTest.perform(steps)
Expand Down
4 changes: 2 additions & 2 deletions Tests/IncrementalTestFramework/AddOn.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
/// For example the line `var gazorp //# initGazorp = 17`
/// will normall be compiled as written. But if the `Step` includes `initGazorp` in its `addOns`
/// the line passed to the compiler will be `var gazorp = 17`
struct AddOn {
public struct AddOn {
/// The name of the `AddOn`. That is, the identifier in the above description.
let name: String
public let name: String

init(named name: String) {
self.name = name
Expand Down
Loading