Skip to content

Commit e867d2f

Browse files
committed
Test MVar and Chan like Haskell
1 parent 8ee6032 commit e867d2f

File tree

5 files changed

+334
-169
lines changed

5 files changed

+334
-169
lines changed

Concurrent.xcodeproj/project.pbxproj

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
8434F9B819E9CDC0008D9909 /* MVar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9A519E9CDC0008D9909 /* MVar.swift */; };
2727
8434F9B919E9CDC0008D9909 /* QSem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9A619E9CDC0008D9909 /* QSem.swift */; };
2828
8434F9D019E9CE1D008D9909 /* PiCalculus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9CF19E9CE1D008D9909 /* PiCalculus.swift */; };
29-
8434F9D419E9CE28008D9909 /* ConcurrentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9D319E9CE28008D9909 /* ConcurrentTests.swift */; };
3029
8475205A1A7EC3E800975C8B /* Exception.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847520591A7EC3E800975C8B /* Exception.swift */; };
3130
849AA3AF1A09850400472F08 /* Concurrent.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8434F98019E9CD76008D9909 /* Concurrent.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3231
84A53D451B1A8F1800E8A107 /* Concurrent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84A53D3A1B1A8F1800E8A107 /* Concurrent.framework */; };
@@ -41,8 +40,6 @@
4140
84A53D5F1B1A8F7000E8A107 /* QSem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9A619E9CDC0008D9909 /* QSem.swift */; };
4241
84A53D681B1A8F7000E8A107 /* CONCRealWorld.m in Sources */ = {isa = PBXBuildFile; fileRef = 8434F99F19E9CDC0008D9909 /* CONCRealWorld.m */; };
4342
84A53D6B1B1A8F8B00E8A107 /* Concurrent.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84A53D3A1B1A8F1800E8A107 /* Concurrent.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
44-
84A53D6D1B1A8FC400E8A107 /* ConcurrentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9D319E9CE28008D9909 /* ConcurrentTests.swift */; };
45-
84A53D6E1B1A8FC400E8A107 /* STMSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE01E81A881FE6006D3EEA /* STMSpec.swift */; };
4643
84A53D6F1B1A8FC400E8A107 /* PiCalculus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8434F9CF19E9CE1D008D9909 /* PiCalculus.swift */; };
4744
84A53DDB1B1A965700E8A107 /* Swiftz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84A53DD21B1A964400E8A107 /* Swiftz.framework */; };
4845
84A53DDE1B1A965F00E8A107 /* Swiftz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84A53DD21B1A964400E8A107 /* Swiftz.framework */; };
@@ -51,8 +48,9 @@
5148
84A53DE11B1A966E00E8A107 /* Concurrent-ObjCBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = 8434F9A019E9CDC0008D9909 /* Concurrent-ObjCBridge.h */; };
5249
84A53DED1B1A9C6400E8A107 /* Swiftz.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84A53DD61B1A964400E8A107 /* Swiftz.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5350
84A53E081B1A9F2C00E8A107 /* SwiftCheck.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84A53E011B1A9F1900E8A107 /* SwiftCheck.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51+
84EA2C591B23AE5D0001FB3F /* ChanSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F467811B1B9244002A4C4C /* ChanSpec.swift */; };
52+
84EA2C5A1B23AE5F0001FB3F /* MVarSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841022531B1AA00D00E799A0 /* MVarSpec.swift */; };
5453
84F467831B1B9244002A4C4C /* ChanSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F467811B1B9244002A4C4C /* ChanSpec.swift */; };
55-
84FE01E91A881FE6006D3EEA /* STMSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FE01E81A881FE6006D3EEA /* STMSpec.swift */; };
5654
/* End PBXBuildFile section */
5755

5856
/* Begin PBXContainerItemProxy section */
@@ -202,14 +200,12 @@
202200
8434F9A619E9CDC0008D9909 /* QSem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QSem.swift; sourceTree = "<group>"; };
203201
8434F9CF19E9CE1D008D9909 /* PiCalculus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PiCalculus.swift; sourceTree = "<group>"; };
204202
8434F9D119E9CE24008D9909 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
205-
8434F9D319E9CE28008D9909 /* ConcurrentTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConcurrentTests.swift; sourceTree = "<group>"; };
206203
847520591A7EC3E800975C8B /* Exception.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Exception.swift; sourceTree = "<group>"; };
207204
84A53D3A1B1A8F1800E8A107 /* Concurrent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Concurrent.framework; sourceTree = BUILT_PRODUCTS_DIR; };
208205
84A53D441B1A8F1800E8A107 /* Concurrent-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Concurrent-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
209206
84A53DC91B1A964300E8A107 /* Swiftz.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Swiftz.xcodeproj; path = Carthage/Checkouts/Swiftz/Swiftz.xcodeproj; sourceTree = "<group>"; };
210207
84A53DF51B1A9F1800E8A107 /* SwiftCheck.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SwiftCheck.xcodeproj; path = Carthage/Checkouts/SwiftCheck/SwiftCheck.xcodeproj; sourceTree = SOURCE_ROOT; };
211208
84F467811B1B9244002A4C4C /* ChanSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChanSpec.swift; sourceTree = "<group>"; };
212-
84FE01E81A881FE6006D3EEA /* STMSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = STMSpec.swift; sourceTree = "<group>"; };
213209
/* End PBXFileReference section */
214210

215211
/* Begin PBXFrameworksBuildPhase section */
@@ -303,10 +299,8 @@
303299
8434F98F19E9CD76008D9909 /* ConcurrentTests */ = {
304300
isa = PBXGroup;
305301
children = (
306-
8434F9D319E9CE28008D9909 /* ConcurrentTests.swift */,
307302
84F467811B1B9244002A4C4C /* ChanSpec.swift */,
308303
841022531B1AA00D00E799A0 /* MVarSpec.swift */,
309-
84FE01E81A881FE6006D3EEA /* STMSpec.swift */,
310304
8434F9CF19E9CE1D008D9909 /* PiCalculus.swift */,
311305
8434F99019E9CD76008D9909 /* Supporting Files */,
312306
);
@@ -613,9 +607,9 @@
613607
isa = PBXSourcesBuildPhase;
614608
buildActionMask = 2147483647;
615609
files = (
616-
8434F9D419E9CE28008D9909 /* ConcurrentTests.swift in Sources */,
617610
8434F9D019E9CE1D008D9909 /* PiCalculus.swift in Sources */,
618-
84FE01E91A881FE6006D3EEA /* STMSpec.swift in Sources */,
611+
84EA2C5A1B23AE5F0001FB3F /* MVarSpec.swift in Sources */,
612+
84EA2C591B23AE5D0001FB3F /* ChanSpec.swift in Sources */,
619613
);
620614
runOnlyForDeploymentPostprocessing = 0;
621615
};
@@ -640,8 +634,6 @@
640634
isa = PBXSourcesBuildPhase;
641635
buildActionMask = 2147483647;
642636
files = (
643-
84A53D6D1B1A8FC400E8A107 /* ConcurrentTests.swift in Sources */,
644-
84A53D6E1B1A8FC400E8A107 /* STMSpec.swift in Sources */,
645637
84F467831B1B9244002A4C4C /* ChanSpec.swift in Sources */,
646638
841022551B1AA00D00E799A0 /* MVarSpec.swift in Sources */,
647639
84A53D6F1B1A8FC400E8A107 /* PiCalculus.swift in Sources */,

ConcurrentTests/ChanSpec.swift

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
//
2+
// ChanSpec.swift
3+
// Concurrent
4+
//
5+
// Created by Robert Widmann on 5/30/15.
6+
// Copyright (c) 2015 TypeLift. All rights reserved.
7+
//
8+
9+
import Concurrent
10+
import XCTest
11+
import SwiftCheck
12+
import Swiftz
13+
14+
private enum Action {
15+
case NewChan
16+
case ReadChan
17+
case WriteChan(Int)
18+
case IsEmptyChan
19+
case ReturnInt(Int)
20+
case ReturnBool(Bool)
21+
}
22+
23+
// Here to make the typechecker happy. Do not invoke these.
24+
extension Action : Arbitrary {
25+
static func arbitrary() -> Gen<Action> { return error("Cannot generate arbitrary Action.") }
26+
static func shrink(_ : Action) -> [Action] { return [] }
27+
}
28+
29+
/// This spec is a faithful translation of GHC's Chan tests (except for some Gen stuff relying on
30+
/// lazy lists).
31+
/// ~(https://github.com/ghc/ghc/blob/master/libraries/base/tests/Concurrent/Chan001.hs)
32+
class ChanSpec : XCTestCase {
33+
func testProperties() {
34+
property[""] = formulate([.NewChan, .IsEmptyChan], [.NewChan, .ReturnBool(true)])
35+
36+
property[""] = forAll { (n : Int) in
37+
return self.formulate([.NewChan, .WriteChan(n), .IsEmptyChan], [.NewChan, .WriteChan(n), .ReturnBool(false)])
38+
}
39+
40+
property[""] = forAll { (n : Int) in
41+
return self.formulate([.NewChan, .WriteChan(n), .ReadChan], [.NewChan, .ReturnInt(n)])
42+
}
43+
}
44+
45+
// Calculates the number of items in the channel at the end of executing the list of actions.
46+
private func delta(i : Int, ac : [Action]) -> Int {
47+
if let x = ac.first {
48+
let xs = [Action](ac[1..<ac.endIndex])
49+
switch x {
50+
case .ReadChan:
51+
return self.delta((i == 0) ? error("read on empty MVar") : (i - 1), ac: xs)
52+
case .IsEmptyChan:
53+
fallthrough
54+
case .ReturnInt(_):
55+
fallthrough
56+
case .ReturnBool(_):
57+
fallthrough
58+
case .IsEmptyChan:
59+
return self.delta(i, ac: xs)
60+
case .WriteChan(_):
61+
return self.delta(i+1, ac: xs)
62+
case .NewChan:
63+
return self.delta(0, ac: xs)
64+
}
65+
}
66+
return i
67+
}
68+
69+
// Based on the given number of items, produce an item-neutral sequence of fluff actions.
70+
private func actionsGen(var empty : Int) -> Gen<ArrayOf<Action>> {
71+
if empty == 0 {
72+
return Gen.pure(ArrayOf([]))
73+
}
74+
75+
var result = [Action]()
76+
while empty-- != 0 {
77+
let branch = rand() % 3
78+
if branch == 0 {
79+
return Gen.pure(ArrayOf(Array(count: empty, repeatedValue: .ReadChan) + result))
80+
} else if branch == 1 {
81+
result = [.IsEmptyChan] + result + [.ReadChan]
82+
} else {
83+
result = [.WriteChan(Int.arbitrary().generate)] + result + [.ReadChan]
84+
}
85+
}
86+
return Gen.pure(ArrayOf(result))
87+
}
88+
89+
private func perform(mv : Chan<Int>, _ ac : [Action]) -> ([Bool], [Int]) {
90+
if let x = ac.first {
91+
let xs = [Action](ac[1..<ac.endIndex])
92+
93+
switch x {
94+
case .ReturnInt(let v):
95+
let (b, l) = perform(mv, xs)
96+
return (b, [v] + l)
97+
case .ReturnBool(let v):
98+
let (b, l) = perform(mv, xs)
99+
return ([v] + b, l)
100+
case .ReadChan:
101+
let v = mv.read()
102+
let (b, l) = perform(mv, xs)
103+
return (b, [v] + l)
104+
case .WriteChan(let n):
105+
mv.write(n)
106+
return perform(mv, xs)
107+
case .IsEmptyChan:
108+
let v = mv.isEmpty
109+
let (b, l) = perform(mv, xs)
110+
return ([v] + b, l)
111+
default:
112+
return error("Fatal: Creating a new channel in the middle of a performance is forbidden")
113+
}
114+
}
115+
return ([], [])
116+
}
117+
118+
private func setupPerformance(ac : [Action]) -> ([Bool], [Int]) {
119+
if let x = ac.first {
120+
let xs = [Action](ac[1..<ac.endIndex])
121+
122+
switch x {
123+
case .ReturnInt(let v):
124+
let (b, l) = setupPerformance(xs)
125+
return (b, [v] + l)
126+
case .ReturnBool(let v):
127+
let (b, l) = setupPerformance(xs)
128+
return ([v] + b, l)
129+
case .NewChan:
130+
return perform(Chan(), xs)
131+
default:
132+
return error("Fatal: NewChan must be the first action")
133+
}
134+
}
135+
return ([], [])
136+
}
137+
138+
139+
private func formulate(c : [Action], _ d : [Action]) -> Property {
140+
return forAll(actionsGen(delta(0, ac: c))) { suff in
141+
let (b1, l1) = self.setupPerformance(c + suff.getArray)
142+
let (b2, l2) = self.setupPerformance(d + suff.getArray)
143+
return
144+
((b1 == b2) <?> "Boolean Values Match")
145+
^&&^
146+
((l1 == l2) <?> "MVar Values Match")
147+
}
148+
}
149+
}

ConcurrentTests/ConcurrentTests.swift

Lines changed: 0 additions & 103 deletions
This file was deleted.

0 commit comments

Comments
 (0)