Skip to content
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

Test isolation fixes #112

Merged
merged 2 commits into from
Oct 30, 2023
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
Prev Previous commit
MockFileManager Add requireAllClosuresToBeCalled parameter and fix th…
…read safety
  • Loading branch information
AnthonyMDev committed Oct 30, 2023
commit 07a0f7f94f1fe000b138dc12a8fb78cba0e6b761
60 changes: 42 additions & 18 deletions Tests/ApolloCodegenInternalTestHelpers/MockFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,33 @@ public class MockApolloFileManager: ApolloFileManager {
}
}

/// If `true` then all called closures must be mocked otherwise the call will fail. When `false` any called closure
/// that is not mocked will fall through to `super`. As a byproduct of `false`, all mocked closures must be called otherwise
/// the test will fail.
/// If `true` then any function on the file manager called that is not mocked will
/// result in a test failure. If `false`, any called closure that is not mocked will fall
/// through to `super`. Defaults to `true`.
var strict: Bool { _base.strict }

/// If `true` all mocked closures must be called otherwise the test
/// will fail. Defaults to `true`.
var requireAllClosuresCalled: Bool { _base.requireAllClosuresCalled }
Copy link
Member

Choose a reason for hiding this comment

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

👍🏻


var _base: MockFileManager { unsafeDowncast(base, to: MockFileManager.self) }

/// Designated initializer.
///
/// - Parameters:
/// - strict: If `true` then all called closures must be mocked otherwise the call will fail.
/// When `false` any called closure that is not mocked will fall through to `super`. As a
/// byproduct of `false`, all mocked closures must be called otherwise the test will fail.
public init(strict: Bool = true) {
super.init(base: MockFileManager(strict: strict))
/// - strict: If `true` then any function on the file manager called that is not mocked will
/// result in a test failure. If `false`, any called closure that is not mocked will fall
/// through to `super`. Defaults to `true`.
/// - requireAllClosuresCalled: If `true` all mocked closures must be called otherwise the test
/// will fail. Defaults to `true`.
public init(
strict: Bool = true,
requireAllClosuresCalled: Bool = true
) {
super.init(base: MockFileManager(
strict: strict,
requireAllClosuresCalled: requireAllClosuresCalled
))
}

/// Provide a mock closure for the `FileManager` function.
Expand All @@ -58,39 +71,50 @@ public class MockApolloFileManager: ApolloFileManager {

class MockFileManager: FileManager {

private let lock = NSLock()

fileprivate var closures: [String: Closure] = [:]
fileprivate var closuresToBeCalled: Set<String> = []

/// If `true` then all called closures must be mocked otherwise the call will fail. When `false` any called closure
/// that is not mocked will fall through to `super`. As a byproduct of `false`, all mocked closures must be called otherwise
/// the test will fail.
let strict: Bool

fileprivate init(strict: Bool = true) {
let requireAllClosuresCalled: Bool

fileprivate init(
strict: Bool = true,
requireAllClosuresCalled: Bool = true
) {
self.strict = strict
self.requireAllClosuresCalled = requireAllClosuresCalled
}

deinit {
if strict == false && allClosuresCalled == false {
XCTFail("Non-strict mode requires that all mocked closures are called! Check \(closuresToBeCalled) in your MockFileManager instance.")
if requireAllClosuresCalled && !allClosuresCalled {
XCTFail("`requireAllClosuresCalled` is `true`, but not all mocked closures are called! Check \(closuresToBeCalled) in your MockFileManager instance.")
}
}

/// Provide a mock closure for the `FileManager` function.
///
/// - Parameter closure: The mocked function closure.
fileprivate func mock(closure: Closure) {
closures[closure.description] = closure
closuresToBeCalled.insert(closure.description)
lock.withLock {
closures[closure.description] = closure
closuresToBeCalled.insert(closure.description)
}
}

fileprivate func didCall(closure: Closure) {
closuresToBeCalled.remove(closure.description)
lock.withLock {
_ = closuresToBeCalled.remove(closure.description)
}
}

/// Check whether all mocked closures were called during the lifetime of an instance.
fileprivate var allClosuresCalled: Bool {
return closuresToBeCalled.isEmpty
return lock.withLock {
return closuresToBeCalled.isEmpty
}
}

// MARK: FileManager overrides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class SchemaModuleFileGeneratorTests: XCTestCase {
override func setUp() {
super.setUp()
testFilePathBuilder = TestFilePathBuilder(test: self)
mockFileManager = MockApolloFileManager(strict: true)
mockFileManager = MockApolloFileManager(strict: false)
}

override func tearDown() {
Expand Down Expand Up @@ -122,6 +122,11 @@ class SchemaModuleFileGeneratorTests: XCTestCase {

func test__generate__givenModuleType_other_shouldNotGenerateFile() async throws {
// given
mockFileManager = MockApolloFileManager(
strict: false,
requireAllClosuresCalled: false
)

let configuration = ApolloCodegen.ConfigurationContext(config: ApolloCodegenConfiguration.mock(
.other,
to: rootURL.path
Expand Down
2 changes: 1 addition & 1 deletion Tests/ApolloCodegenTests/FileManagerExtensionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ class FileManagerExtensionTests: XCTestCase {
// given
let filePath = URL(fileURLWithPath: self.uniquePath).path
let directoryPath = URL(fileURLWithPath: self.uniquePath).deletingLastPathComponent().path
let mocked = MockApolloFileManager(strict: true)
let mocked = MockApolloFileManager(strict: true, requireAllClosuresCalled: false)

mocked.mock(closure: .fileExists({ path, isDirectory in
switch path {
Expand Down
Loading