Skip to content

Commit

Permalink
Merge pull request #204 from CodaFi/an-ownership-model
Browse files Browse the repository at this point in the history
Remove dependence on self in ArrowOf and IsoOf
  • Loading branch information
CodaFi authored Dec 1, 2016
2 parents 287dd1a + 44642d8 commit 0e2d31c
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 18 deletions.
40 changes: 22 additions & 18 deletions Sources/Modifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -516,14 +516,15 @@ fileprivate final class ArrowOfImpl<T : Hashable & CoArbitrary, U : Arbitrary> :
}

convenience init(_ arr : @escaping (T) -> U) {
self.init(Dictionary(), { (_ : T) -> U in return undefined() })
var table = [T:U]()
self.init(table, { (_ : T) -> U in return undefined() })

self.arr = { [unowned self] x in
if let v = self.table[x] {
self.arr = { x in
if let v = table[x] {
return v
}
let y = arr(x)
self.table[x] = y
table[x] = y
return y
}
}
Expand Down Expand Up @@ -564,24 +565,25 @@ fileprivate final class IsoOfImpl<T : Hashable & CoArbitrary & Arbitrary, U : Eq
}

convenience init(_ embed : @escaping (T) -> U, _ project : @escaping (U) -> T) {
self.init(Dictionary(), { (_ : T) -> U in return undefined() }, { (_ : U) -> T in return undefined() })
var table = [T:U]()
self.init(table, { (_ : T) -> U in return undefined() }, { (_ : U) -> T in return undefined() })

self.embed = { [unowned self] t in
if let v = self.table[t] {
self.embed = { t in
if let v = table[t] {
return v
}
let y = embed(t)
self.table[t] = y
table[t] = y
return y
}

self.project = { [unowned self] u in
let ts = self.table.filter { $1 == u }.map { $0.0 }
if let k = ts.first, let _ = self.table[k] {
self.project = { u in
let ts = table.filter { $1 == u }.map { $0.0 }
if let k = ts.first, let _ = table[k] {
return k
}
let y = project(u)
self.table[y] = u
table[y] = u
return y
}
}
Expand All @@ -601,17 +603,19 @@ fileprivate final class IsoOfImpl<T : Hashable & CoArbitrary & Arbitrary, U : Eq
static func shrink(_ f : IsoOfImpl<T, U>) -> [IsoOfImpl<T, U>] {
return f.table.flatMap { (x, y) in
return Zip2Sequence(_sequence1: T.shrink(x), _sequence2: U.shrink(y)).map({ (y1 , y2) -> IsoOfImpl<T, U> in
return IsoOfImpl<T, U>({ (z : T) -> U in
if x == z {
return y2
}
return f.embed(z)
return IsoOfImpl<T, U>(
{ (z : T) -> U in
if x == z {
return y2
}
return f.embed(z)
}, { (z : U) -> T in
if y == z {
return y1
}
return f.project(z)
})
}
)
})
}
}
Expand Down
8 changes: 8 additions & 0 deletions SwiftCheck.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
826D818B1C953D070022266C /* WitnessedArbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 826D81571C953D070022266C /* WitnessedArbitrary.swift */; };
826D818C1C953D070022266C /* WitnessedArbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 826D81571C953D070022266C /* WitnessedArbitrary.swift */; };
826D818D1C953D070022266C /* WitnessedArbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 826D81571C953D070022266C /* WitnessedArbitrary.swift */; };
828B7C8A1DEF58CF0066A994 /* FormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 828B7C861DEF575C0066A994 /* FormatterSpec.swift */; };
828B7C8B1DEF58D00066A994 /* FormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 828B7C861DEF575C0066A994 /* FormatterSpec.swift */; };
828B7C8C1DEF58D10066A994 /* FormatterSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 828B7C861DEF575C0066A994 /* FormatterSpec.swift */; };
82BA08CF1D6FFBD20068D32F /* Compose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BA08CE1D6FFBD20068D32F /* Compose.swift */; };
82BA08D01D6FFBD20068D32F /* Compose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BA08CE1D6FFBD20068D32F /* Compose.swift */; };
82BA08D11D6FFBD20068D32F /* Compose.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82BA08CE1D6FFBD20068D32F /* Compose.swift */; };
Expand Down Expand Up @@ -192,6 +195,7 @@
826D81561C953D070022266C /* Witness.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Witness.swift; path = Sources/Witness.swift; sourceTree = SOURCE_ROOT; };
826D81571C953D070022266C /* WitnessedArbitrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WitnessedArbitrary.swift; path = Sources/WitnessedArbitrary.swift; sourceTree = SOURCE_ROOT; };
826D81C61C953D350022266C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tests/Info.plist; sourceTree = SOURCE_ROOT; };
828B7C861DEF575C0066A994 /* FormatterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FormatterSpec.swift; path = Tests/SwiftCheckTests/FormatterSpec.swift; sourceTree = SOURCE_ROOT; };
82BA08CE1D6FFBD20068D32F /* Compose.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compose.swift; path = Sources/Compose.swift; sourceTree = SOURCE_ROOT; };
844FCC8D198B320500EB242A /* SwiftCheck.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftCheck.framework; sourceTree = BUILT_PRODUCTS_DIR; };
844FCC98198B320500EB242A /* SwiftCheckTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCheckTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -317,6 +321,7 @@
825C9AD11D8EE86E003313E1 /* ComplexSpec.swift */,
825C9AD21D8EE86E003313E1 /* DiscardSpec.swift */,
825C9AD31D8EE86E003313E1 /* FailureSpec.swift */,
828B7C861DEF575C0066A994 /* FormatterSpec.swift */,
825C9AD41D8EE86E003313E1 /* GenSpec.swift */,
825C9AD51D8EE86E003313E1 /* LambdaSpec.swift */,
825C9AD61D8EE86E003313E1 /* ModifierSpec.swift */,
Expand Down Expand Up @@ -649,6 +654,7 @@
825C9AE31D8EE86E003313E1 /* ComplexSpec.swift in Sources */,
825C9AE01D8EE86E003313E1 /* BooleanIdentitySpec.swift in Sources */,
825C9AF21D8EE86E003313E1 /* ModifierSpec.swift in Sources */,
828B7C8C1DEF58D10066A994 /* FormatterSpec.swift in Sources */,
825C9AF81D8EE86E003313E1 /* PropertySpec.swift in Sources */,
825C9AFE1D8EE86E003313E1 /* ReplaySpec.swift in Sources */,
);
Expand Down Expand Up @@ -694,6 +700,7 @@
825C9AE11D8EE86E003313E1 /* ComplexSpec.swift in Sources */,
825C9ADE1D8EE86E003313E1 /* BooleanIdentitySpec.swift in Sources */,
825C9AF01D8EE86E003313E1 /* ModifierSpec.swift in Sources */,
828B7C8A1DEF58CF0066A994 /* FormatterSpec.swift in Sources */,
825C9AF61D8EE86E003313E1 /* PropertySpec.swift in Sources */,
825C9AFC1D8EE86E003313E1 /* ReplaySpec.swift in Sources */,
);
Expand Down Expand Up @@ -739,6 +746,7 @@
825C9AE21D8EE86E003313E1 /* ComplexSpec.swift in Sources */,
825C9ADF1D8EE86E003313E1 /* BooleanIdentitySpec.swift in Sources */,
825C9AF11D8EE86E003313E1 /* ModifierSpec.swift in Sources */,
828B7C8B1DEF58D00066A994 /* FormatterSpec.swift in Sources */,
825C9AF71D8EE86E003313E1 /* PropertySpec.swift in Sources */,
825C9AFD1D8EE86E003313E1 /* ReplaySpec.swift in Sources */,
);
Expand Down
83 changes: 83 additions & 0 deletions Tests/SwiftCheckTests/FormatterSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// FormatterSpec.swift
// SwiftCheck
//
// Created by Robert Widmann on 11/30/16.
// Copyright © 2016 Typelift. All rights reserved.
//
// Spec due to broomburgo (https://github.com/broomburgo) meant to test lifetime
// issues in ArrowOf and IsoOf

import SwiftCheck
import XCTest

struct Formatter<Value> {
let lengthLimit : UInt
let makeString : (Value) -> String
let makeValue : (String) -> Value

init(lengthLimit : UInt, makeString : @escaping (Value) -> String, makeValue : @escaping (String) -> Value) {
self.lengthLimit = lengthLimit
self.makeString = makeString
self.makeValue = makeValue
}

func format(_ value : Value) -> String {
let formatted = makeString(value)
let maxIndex = formatted.index(formatted.startIndex, offsetBy: String.IndexDistance(lengthLimit))
if maxIndex >= formatted.endIndex {
return formatted
} else {
return formatted.substring(to: maxIndex)
}
}

func unFormat(_ string : String) -> Value {
let value = makeValue(string)
return value
}
}

struct ArbitraryFormatter<Value : Arbitrary & CoArbitrary & Hashable> : Arbitrary {
private let formatter : Formatter<Value>
init(_ formatter : Formatter<Value>) {
self.formatter = formatter
}

var get : Formatter<Value> {
return formatter
}

static var arbitrary : Gen<ArbitraryFormatter<Value>> {
return Gen.one(of: [
Gen<(UInt, ArrowOf<Value, String>, ArrowOf<String, Value>)>
.zip(UInt.arbitrary, ArrowOf<Value,String>.arbitrary, ArrowOf<String,Value>.arbitrary)
.map { Formatter<Value>(lengthLimit: $0, makeString: $1.getArrow, makeValue: $2.getArrow) }
.map(ArbitraryFormatter.init),
Gen<(UInt, IsoOf<Value, String>)>
.zip(UInt.arbitrary, IsoOf<Value, String>.arbitrary)
.map { Formatter<Value>(lengthLimit: $0, makeString: $1.getTo, makeValue: $1.getFrom) }
.map(ArbitraryFormatter.init)
])
}
}

class FormatterSpec : XCTestCase {
func testAlwaysCorrectLength() {
property(
"Any formatted string is shorter or equal than the provided length"
) <- forAll { (x: Int, af: ArbitraryFormatter<Int>) in
let formatter = af.get
let string = formatter.format(x)
_ = formatter.unFormat(string)
return string.distance(from: string.startIndex, to: string.endIndex) <= String.IndexDistance(formatter.lengthLimit)
}
}


#if !(os(macOS) || os(iOS) || os(watchOS) || os(tvOS))
static var allTests = testCase([
("testAlwaysCorrectLength", testAlwaysCorrectLength),
])
#endif
}

0 comments on commit 0e2d31c

Please sign in to comment.