Skip to content

Commit

Permalink
Merge pull request #25 from RougeWare/bugfix/20-Lazy-property-wrapper…
Browse files Browse the repository at this point in the history
…-eagerly-evaluated

Bodge guarding against issue #20
  • Loading branch information
KyNorthstar authored Aug 2, 2020
2 parents 77d063c + 93bdf54 commit 6981fb1
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 47 deletions.
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ let package = Package(
.testTarget(
name: "LazyContainersTests",
dependencies: ["LazyContainers"]),
]
],

swiftLanguageVersions: [.v5]
)
11 changes: 9 additions & 2 deletions Sources/LazyContainers/LazyContainers.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//
// LazyContainers.swift
// Lazy in Swift 5.1
// Lazy in Swift 5.2
//
// Created by Ben Leggiero on 2018-05-04.
// Version 3.1.0 (last edited 2019-08-30)
// Version 4.0.0 (last edited 2020-08-01)
// Copyright Ben Leggiero © 2020
// https://github.com/RougeWare/Swift-Lazy-Patterns/blob/master/LICENSE.txt
//
Expand Down Expand Up @@ -217,6 +217,13 @@ public struct Lazy<Value>: LazyContainer {
/// Same as `init(initializer:)`, but allows you to use property wrapper andor autoclosure semantics
///
/// - Parameter initializer: The closure that will be called the very first time a value is needed
@available(swift, // https://github.com/RougeWare/Swift-Lazy-Patterns/issues/20
introduced: 5.3,
message: """
Due to changes introduced in Swift 5.3, property wrappers can now be passed their initial value lazily, through the language assignment syntax. This initializer requires that behavior to work properly.
For Swift 5.2.x and earlier, I recommend you don't use `Lazy` as a property wrapper, since Swift 5.2.x property wrappers can't guarantee lazy evaluation.
This is a real downer for me, but at least it appears to have been a fixable bug, rather than a problem with the core feature itself.
""")
public init(wrappedValue initializer: @autoclosure @escaping Initializer<Value>) {
self.init(initializer: initializer)
}
Expand Down
39 changes: 39 additions & 0 deletions Tests/LazyContainersTests/GitHubIssue20Tests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// GitHubIssue20Tests.swift
// LazyContainersTests
//
// Created by Gabe Shahbazian on 2020-07-29.
//

import XCTest
@testable import LazyContainers



#if swift(>=5.3)
var shouldNotRun = false

class ShouldNotInit {
init() {
shouldNotRun = true
}
}



/// Guards against issue #20
/// https://github.com/RougeWare/Swift-Lazy-Patterns/issues/20
final class GitHubIssue20Tests: XCTestCase {

@Lazy
var lazyShouldNotRun = ShouldNotInit()

func testLazyInitWithPropertyWrapper() {
XCTAssertFalse(shouldNotRun)
}

static var allTests = [
("testLazyInitWithPropertyWrapper", testLazyInitWithPropertyWrapper)
]
}
#endif
126 changes: 84 additions & 42 deletions Tests/LazyContainersTests/LazyContainersTests.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//
// LazyContainersTests.swift
// LazyContainersTests
//
// Created by Ben Leggiero on 2019-08-19
// Copyright BH-0-PD © 2019 - https://github.com/BlueHuskyStudios/Licenses/blob/master/Licenses/BH-0-PD.txt
// Copyright Ben Leggiero © 2020
// https://github.com/RougeWare/Swift-Lazy-Patterns/blob/master/LICENSE.txt
//


Expand All @@ -12,22 +14,29 @@ import XCTest



var sideEffect: String?

var sideEffectA: String?
func makeLazyA() -> String {
sideEffect = "Side effect A"
sideEffectA = "Side effect A1"
return "lAzy"
}

var sideEffectB: String?
func makeLazyB() -> String {
sideEffectB = "Side effect B"
return "Lazy B (this time with side-effects)"
}



final class LazyContainersTests: XCTestCase {

#if swift(>=5.3)
@Lazy(initializer: makeLazyA)
var lazyInitWithPropertyWrapper: String
var lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect: String
#endif

var lazyInitTraditionally = Lazy<String>() {
sideEffect = "Side effect B"
sideEffectA = "Side effect A2"
return "lazy B"
}

Expand All @@ -43,7 +52,8 @@ final class LazyContainersTests: XCTestCase {


override func setUp() {
sideEffect = nil
sideEffectA = nil
sideEffectB = nil
}


Expand All @@ -55,43 +65,60 @@ final class LazyContainersTests: XCTestCase {

// MARK: - `Lazy`

func testLazyInitWithPropertyWrapper() {
XCTAssertEqual(sideEffect, nil)
XCTAssertFalse(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, nil)
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
#if swift(>=5.3)
func testLazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect() {
XCTAssertEqual(sideEffectA, nil)
XCTAssertFalse(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, nil)
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")

lazyInitWithPropertyWrapper = "MAnual"
lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect = "MAnual"

XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized)
XCTAssertEqual(sideEffect, "Side effect A")
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect)
XCTAssertEqual(sideEffectA, "Side effect A1")
XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized)
XCTAssertEqual(sideEffectA, "Side effect A1")
}


func testLazyInitWithPropertyWrapperAndSideEffect() {

struct Test {
@Lazy
var lazyInitWithPropertyWrapperAndSideEffect = makeLazyB()
}


let test = Test()

XCTAssertNil(sideEffectB, "@Lazy eagerly evaluated its initial value")
XCTAssertEqual(test.lazyInitWithPropertyWrapperAndSideEffect, "Lazy B (this time with side-effects)")
}
#endif


func testLazyInitTraditionally() {
XCTAssertFalse(lazyInitTraditionally.isInitialized)
XCTAssertEqual("lazy B", lazyInitTraditionally.wrappedValue)
Expand Down Expand Up @@ -249,9 +276,16 @@ final class LazyContainersTests: XCTestCase {
XCTAssertEqual("Manual F", functionalLazyInitTraditionally.wrappedValue)
XCTAssertTrue(functionalLazyInitTraditionally.isInitialized)
}

static var allTests = [
("testLazyInitWithPropertyWrapper", testLazyInitWithPropertyWrapper),

#if swift(>=5.3)
static let testsWhichRequireSwift5_3 = [
("testLazyInitWithPropertyWrapperWithCustomInitializerAndSideEffect", testLazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect),
("testLazyInitWithPropertyWrapperAndSideEffect", testLazyInitWithPropertyWrapperAndSideEffect),
]
#endif


static let testsWhichWorkBeforeSwift5_3 = [
("testLazyInitTraditionally", testLazyInitTraditionally),

("testResettableLazyInitWithPropertyWrapper", testResettableLazyInitWithPropertyWrapper),
Expand All @@ -260,4 +294,12 @@ final class LazyContainersTests: XCTestCase {
("testFunctionalLazyInitWithPropertyWrapper", testFunctionalLazyInitWithPropertyWrapper),
("testFunctionalLazyInitTraditionally", testFunctionalLazyInitTraditionally),
]


#if swift(>=5.3)
static let allTests = testsWhichRequireSwift5_3 + testsWhichWorkBeforeSwift5_3
#else
@inline(__always)
static let allTests = testsWhichWorkBeforeSwift5_3
#endif
}
1 change: 1 addition & 0 deletions Tests/LazyContainersTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import XCTest
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(LazyContainersTests.allTests),
testCase(GitHubIssue20Tests.allTests),
]
}
#endif
7 changes: 5 additions & 2 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import XCTest

import LazyContainersTests

var tests = [XCTestCaseEntry]()
tests += LazyContainersTests.allTests()
let tests: [XCTestCaseEntry] = [
LazyContainersTests.allTests,
GitHubIssue20Tests.allTests,
]

XCTMain(tests)

0 comments on commit 6981fb1

Please sign in to comment.