Skip to content

Commit d14b330

Browse files
authored
Merge pull request #1830 from apollographql/fix-xctest-memory-leaks
Fix memory leaks due to xctest
2 parents 10a43e4 + d257311 commit d14b330

30 files changed

Lines changed: 340 additions & 182 deletions

Sources/ApolloTestSupport/TestCacheProvider.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ public class InMemoryTestCacheProvider: TestCacheProvider {
1717

1818
public protocol CacheDependentTesting {
1919
var cacheType: TestCacheProvider.Type { get }
20-
var cache: NormalizedCache! { get }
21-
var defaultWaitTimeout: TimeInterval { get }
20+
var cache: NormalizedCache! { get }
2221
}
2322

2423
extension CacheDependentTesting where Self: XCTestCase {

Sources/ApolloTestSupport/XCTAssertHelpers.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,41 @@ public func XCTAssertFailureResult<Success>(_ expression: @autoclosure () throws
7575
}
7676
}
7777

78+
/// Checks that the condition is eventually true with a given timeout (default 1 second).
79+
///
80+
/// This assertion runs the run loop for 0.01 second after each time it checks the condition until
81+
/// the condition is true or the timeout is reached.
82+
///
83+
/// - Parameters:
84+
/// - test: An autoclosure for the condition to test for truthiness.
85+
/// - timeout: The timeout, at which point the test will fail. Defaults to 1 second.
86+
/// - message: A message to send on failure.
87+
public func XCTAssertTrueEventually(_ test: @autoclosure () -> Bool, timeout: TimeInterval = 1.0, message: String = "", file: StaticString = #file, line: UInt = #line) {
88+
let runLoop = RunLoop.current
89+
let timeoutDate = Date(timeIntervalSinceNow: timeout)
90+
repeat {
91+
if test() {
92+
return
93+
}
94+
runLoop.run(until: Date(timeIntervalSinceNow: 0.01))
95+
} while Date().compare(timeoutDate) == .orderedAscending
96+
97+
XCTFail(message, file: file, line: line)
98+
}
99+
100+
/// Checks that the condition is eventually false with a given timeout (default 1 second).
101+
///
102+
/// This assertion runs the run loop for 0.01 second after each time it checks the condition until
103+
/// the condition is false or the timeout is reached.
104+
///
105+
/// - Parameters:
106+
/// - test: An autoclosure for the condition to test for falsiness.
107+
/// - timeout: The timeout, at which point the test will fail. Defaults to 1 second.
108+
/// - message: A message to send on failure.
109+
public func XCTAssertFalseEventually(_ test: @autoclosure () -> Bool, timeout: TimeInterval = 1.0, message: String = "", file: StaticString = #file, line: UInt = #line) {
110+
XCTAssertTrueEventually(!test(), timeout: timeout, message: message, file: file, line: line)
111+
}
112+
78113
/// Downcast an expression to a specified type.
79114
///
80115
/// Generates a failure when the downcast doesn't succeed.

Sources/ApolloTestSupport/XCTestCase+Helpers.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,14 @@ public extension XCTestCase {
5353
}
5454

5555
public protocol StoreLoading {
56-
var defaultWaitTimeout: TimeInterval { get }
56+
static var defaultWaitTimeout: TimeInterval { get }
5757
var store: ApolloStore! { get }
5858
}
5959

60+
public extension StoreLoading {
61+
static var defaultWaitTimeout: TimeInterval { 1.0 }
62+
}
63+
6064
extension StoreLoading where Self: XCTestCase {
6165
public func loadFromStore<Operation: GraphQLOperation>(
6266
query: Operation,
@@ -70,6 +74,6 @@ extension StoreLoading where Self: XCTestCase {
7074

7175
store.load(query: query, resultHandler: resultObserver.handler)
7276

73-
wait(for: [expectation], timeout: defaultWaitTimeout)
77+
wait(for: [expectation], timeout: Self.defaultWaitTimeout)
7478
}
7579
}

Tests/ApolloCodegenTests/ASTParsingTests.swift

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import ApolloCodegenTestSupport
1212

1313
class ASTParsingTests: XCTestCase {
1414

15-
lazy var starWarsJSONURL: URL = {
15+
static let starWarsJSONURL: URL = {
1616
let sourceRoot = CodegenTestHelper.sourceRootURL()
1717
let starWarsJSONURL = sourceRoot
1818
.appendingPathComponent("Sources")
@@ -23,7 +23,6 @@ class ASTParsingTests: XCTestCase {
2323
return starWarsJSONURL
2424
}()
2525

26-
2726
private func loadAST(from url: URL,
2827
file: StaticString = #file,
2928
line: UInt = #line) throws -> ASTOutput {
@@ -32,7 +31,7 @@ class ASTParsingTests: XCTestCase {
3231

3332
func testLoadingStarWarsJSON() throws {
3433
do {
35-
let output = try loadAST(from: starWarsJSONURL)
34+
let output = try loadAST(from: Self.starWarsJSONURL)
3635
XCTAssertEqual(output.operations.count, 38)
3736
XCTAssertEqual(output.fragments.count, 15)
3837
XCTAssertEqual(output.typesUsed.count, 3)
@@ -46,7 +45,7 @@ class ASTParsingTests: XCTestCase {
4645
func testParsingASTUnionTypes() throws {
4746
let output: ASTOutput
4847
do {
49-
output = try loadAST(from: starWarsJSONURL)
48+
output = try loadAST(from: Self.starWarsJSONURL)
5049
} catch {
5150
CodegenTestHelper.handleFileLoadError(error)
5251
return
@@ -68,7 +67,7 @@ class ASTParsingTests: XCTestCase {
6867
func testParsingASTInterfaceTypes() throws {
6968
let output: ASTOutput
7069
do {
71-
output = try loadAST(from: starWarsJSONURL)
70+
output = try loadAST(from: Self.starWarsJSONURL)
7271
} catch {
7372
CodegenTestHelper.handleFileLoadError(error)
7473
return
@@ -89,7 +88,7 @@ class ASTParsingTests: XCTestCase {
8988
func testParsingASTInputTypes() throws {
9089
let output: ASTOutput
9190
do {
92-
output = try loadAST(from: starWarsJSONURL)
91+
output = try loadAST(from: Self.starWarsJSONURL)
9392
} catch {
9493
CodegenTestHelper.handleFileLoadError(error)
9594
return
@@ -189,7 +188,7 @@ class ASTParsingTests: XCTestCase {
189188
func testParsingOperationWithMutation() throws {
190189
let output: ASTOutput
191190
do {
192-
output = try loadAST(from: starWarsJSONURL)
191+
output = try loadAST(from: Self.starWarsJSONURL)
193192
} catch {
194193
CodegenTestHelper.handleFileLoadError(error)
195194
return
@@ -291,7 +290,7 @@ mutation CreateAwesomeReview {\n createReview(episode: JEDI, review: {stars: 10
291290
func testParsingOperationWithQueryAndInputAndNestedTypes() throws {
292291
let output: ASTOutput
293292
do {
294-
output = try loadAST(from: starWarsJSONURL)
293+
output = try loadAST(from: Self.starWarsJSONURL)
295294
} catch {
296295
CodegenTestHelper.handleFileLoadError(error)
297296
return
@@ -454,7 +453,7 @@ query HeroAndFriendsNames($episode: Episode) {\n hero(episode: $episode) {\n
454453
func testParsingOperationWithQueryAndFragment() throws {
455454
let output: ASTOutput
456455
do {
457-
output = try loadAST(from: starWarsJSONURL)
456+
output = try loadAST(from: Self.starWarsJSONURL)
458457
} catch {
459458
CodegenTestHelper.handleFileLoadError(error)
460459
return
@@ -631,7 +630,7 @@ query HeroAndFriendsNamesWithFragment($episode: Episode) {\n hero(episode: $epi
631630
func testParsingQueryWithInlineFragments() throws {
632631
let output: ASTOutput
633632
do {
634-
output = try loadAST(from: starWarsJSONURL)
633+
output = try loadAST(from: Self.starWarsJSONURL)
635634
} catch {
636635
CodegenTestHelper.handleFileLoadError(error)
637636
return
@@ -801,7 +800,7 @@ query HeroDetails($episode: Episode) {\n hero(episode: $episode) {\n __typen
801800
func testParsingQueryWithAliasesAndPassedInRawValue() throws {
802801
let output: ASTOutput
803802
do {
804-
output = try loadAST(from: starWarsJSONURL)
803+
output = try loadAST(from: Self.starWarsJSONURL)
805804
} catch {
806805
CodegenTestHelper.handleFileLoadError(error)
807806
return
@@ -969,7 +968,7 @@ query TwoHeroes {\n r2: hero {\n __typename\n name\n }\n luke: hero(epi
969968
func testParsingQueryWithConditionalInclusion() throws {
970969
let output: ASTOutput
971970
do {
972-
output = try loadAST(from: starWarsJSONURL)
971+
output = try loadAST(from: Self.starWarsJSONURL)
973972
} catch {
974973
CodegenTestHelper.handleFileLoadError(error)
975974
return
@@ -1061,7 +1060,7 @@ query HeroNameConditionalInclusion($includeName: Boolean!) {\n hero {\n __ty
10611060
func testParsingQueryWithConditionalExclusion() throws {
10621061
let output: ASTOutput
10631062
do {
1064-
output = try loadAST(from: starWarsJSONURL)
1063+
output = try loadAST(from: Self.starWarsJSONURL)
10651064
} catch {
10661065
CodegenTestHelper.handleFileLoadError(error)
10671066
return
@@ -1152,7 +1151,7 @@ query HeroNameConditionalExclusion($skipName: Boolean!) {\n hero {\n __typen
11521151
func testParsingQueryWithConditionalFragmentInclusion() throws {
11531152
let output: ASTOutput
11541153
do {
1155-
output = try loadAST(from: starWarsJSONURL)
1154+
output = try loadAST(from: Self.starWarsJSONURL)
11561155
} catch {
11571156
CodegenTestHelper.handleFileLoadError(error)
11581157
return

Tests/ApolloCodegenTests/Frontend/CompilationTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ class CompilationTests: XCTestCase {
1717

1818
schema = try codegenFrontend.loadSchemaFromIntrospectionResult(introspectionResult)
1919
}
20+
21+
override func tearDown() {
22+
codegenFrontend = nil
23+
schema = nil
24+
25+
super.tearDown()
26+
}
2027

2128
func testCompileSingleQuery() throws {
2229
let source = try codegenFrontend.makeSource("""

Tests/ApolloCodegenTests/Frontend/DocumentParsingAndValidationTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ class DocumentParsingAndValidationTests: XCTestCase {
1717

1818
schema = try codegenFrontend.loadSchemaFromIntrospectionResult(introspectionResult)
1919
}
20+
21+
override func tearDown() {
22+
codegenFrontend = nil
23+
schema = nil
24+
25+
super.tearDown()
26+
}
2027

2128
func testParseDocument() throws {
2229
let source = try codegenFrontend.makeSource("""

Tests/ApolloCodegenTests/Frontend/SchemaIntrospectionTests.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ class SchemaIntrospectionTests: XCTestCase {
1616
let introspectionResult = try String(contentsOf: XCTUnwrap(starWarsAPIBundle.url(forResource: "schema", withExtension: "json")))
1717
schema = try codegenFrontend.loadSchemaFromIntrospectionResult(introspectionResult)
1818
}
19+
20+
override func tearDown() {
21+
codegenFrontend = nil
22+
schema = nil
23+
24+
super.tearDown()
25+
}
1926

2027
func testGetFieldsForObjectType() throws {
2128
let droidType = try XCTDowncast(XCTUnwrap(schema.getType(named: "Droid")), to: GraphQLObjectType.self)

Tests/ApolloCodegenTests/Frontend/SchemaLoadingTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ class SchemaLoadingTests: XCTestCase {
1212

1313
codegenFrontend = try ApolloCodegenFrontend()
1414
}
15+
16+
override func tearDown() {
17+
codegenFrontend = nil
18+
19+
super.tearDown()
20+
}
1521

1622
func testParseSchemaFromIntrospectionResult() throws {
1723
let introspectionResult = try String(contentsOf: XCTUnwrap(starWarsAPIBundle.url(forResource: "schema", withExtension: "json")))

Tests/ApolloServerIntegrationTests/LegacyInterceptorProviderIntegrationTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class LegacyInterceptorProviderIntegrationTests: XCTestCase {
2424

2525
override func tearDown() {
2626
legacyClient = nil
27+
28+
super.tearDown()
2729
}
2830

2931
func testLoading() {

Tests/ApolloServerIntegrationTests/StarWarsServerCachingRoundtripTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class StarWarsServerCachingRoundtripTests: XCTestCase, CacheDependentTesting {
1414
InMemoryTestCacheProvider.self
1515
}
1616

17-
var defaultWaitTimeout: TimeInterval = 5
17+
static let defaultWaitTimeout: TimeInterval = 5
1818

1919
var cache: NormalizedCache!
2020
var store: ApolloStore!
@@ -82,7 +82,7 @@ class StarWarsServerCachingRoundtripTests: XCTestCase, CacheDependentTesting {
8282
XCTAssertSuccessResult(result, file: file, line: line)
8383
}
8484

85-
wait(for: [fetchedFromServerExpectation], timeout: defaultWaitTimeout)
85+
wait(for: [fetchedFromServerExpectation], timeout: Self.defaultWaitTimeout)
8686

8787
let resultObserver = makeResultObserver(for: query, file: file, line: line)
8888

@@ -98,6 +98,6 @@ class StarWarsServerCachingRoundtripTests: XCTestCase, CacheDependentTesting {
9898

9999
store.load(query: query, resultHandler: resultObserver.handler)
100100

101-
wait(for: [loadedFromStoreExpectation], timeout: defaultWaitTimeout)
101+
wait(for: [loadedFromStoreExpectation], timeout: Self.defaultWaitTimeout)
102102
}
103103
}

0 commit comments

Comments
 (0)