1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
// REQUIRES: objc_interop
import StdlibUnittest
import ObjectiveC // for autoreleasepool
var suite = TestSuite("AutoreleasingUnsafeMutablePointer")
/// Call `body` passing it an AutoreleasingUnsafeMutablePointer whose pointee
/// has the specified value, allocated in a temporary buffer.
func withAUMP<Pointee: AnyObject>(
as type: Pointee.Type = Pointee.self,
initialValue: Optional<Unmanaged<Pointee>> = nil,
_ body: (AutoreleasingUnsafeMutablePointer<Optional<Pointee>>) -> Void
) {
let p =
UnsafeMutablePointer<Optional<Unmanaged<Pointee>>>.allocate(capacity: 1)
p.initialize(to: initialValue)
body(AutoreleasingUnsafeMutablePointer(p))
p.deallocate()
}
/// Call `body` passing it an AutoreleasingUnsafeMutablePointer whose pointee
/// has the specified value, allocated in a temporary buffer.
func withAUMP<Pointee: AnyObject>(
initialValues: [Unmanaged<Pointee>?],
_ body: (AutoreleasingUnsafeMutablePointer<Optional<Pointee>>) -> Void
) {
let p = UnsafeMutablePointer<Optional<Unmanaged<Pointee>>>.allocate(
capacity: initialValues.count
)
for i in 0 ..< initialValues.count {
(p + i).initialize(to: initialValues[i])
}
let aump = AutoreleasingUnsafeMutablePointer<Optional<Pointee>>(p)
body(aump)
p.deallocate()
}
suite.test("helper calls its closure exactly once") {
// `withAUMP` is expected to call its closure exactly once, with an argument
// pointing to a nil value.
var runCount = 0
withAUMP(as: LifetimeTracked.self) { p in
runCount += 1
expectNil(p.pointee)
}
expectEqual(runCount, 1)
}
suite.test("init doesn't autorelease") {
let originalInstances = LifetimeTracked.instances
let unmanaged = Unmanaged.passRetained(LifetimeTracked(42))
expectEqual(LifetimeTracked.instances, originalInstances + 1)
withAUMP(initialValue: unmanaged) { p in noop(p) }
// Releasing the original reference should immediately deallocate the
// object.
expectEqual(LifetimeTracked.instances, originalInstances + 1)
unmanaged.release()
expectEqual(LifetimeTracked.instances, originalInstances)
}
suite.test("getter initially returns the initial value") {
withAUMP(as: LifetimeTracked.self) { p in
expectNil(p.pointee)
}
let unmanaged = Unmanaged.passRetained(LifetimeTracked(42))
withAUMP(initialValue: unmanaged) { p in
expectTrue(p.pointee === unmanaged.takeUnretainedValue())
}
unmanaged.release()
}
suite.test("pointee getter returns the last value set") {
autoreleasepool {
withAUMP(as: LifetimeTracked.self) { p in
expectNil(p.pointee)
let object = LifetimeTracked(23)
p.pointee = object
expectTrue(p.pointee === object)
let other = LifetimeTracked(42)
p.pointee = other
expectTrue(p.pointee === other)
p.pointee = nil
expectNil(p.pointee)
}
}
}
suite.test("pointee getter doesn't autorelease") {
let originalInstances = LifetimeTracked.instances
autoreleasepool {
let unmanaged = Unmanaged.passRetained(LifetimeTracked(42))
expectEqual(LifetimeTracked.instances, originalInstances + 1)
withAUMP(initialValue: unmanaged) { p in
expectTrue(p.pointee === unmanaged.takeUnretainedValue())
}
// Releasing the original reference should immediately deallocate the
// object.
expectEqual(LifetimeTracked.instances, originalInstances + 1)
unmanaged.release()
expectEqual(LifetimeTracked.instances, originalInstances)
}
}
suite.test("pointee setter autoreleases the new value") {
let originalInstances = LifetimeTracked.instances
autoreleasepool {
withAUMP(as: LifetimeTracked.self) { p in
let object = LifetimeTracked(42)
// There is one more instance now.
expectEqual(LifetimeTracked.instances, originalInstances + 1)
p.pointee = object
}
// The strong reference to `object` is gone, but the autoreleasepool
// is still holding onto it.
expectEqual(LifetimeTracked.instances, originalInstances + 1)
}
// Draining the pool deallocates the instance.
expectEqual(LifetimeTracked.instances, originalInstances)
}
suite.test("AutoreleasingUnsafeMutablePointer doesn't retain its value") {
let originalInstances = LifetimeTracked.instances
withAUMP(as: LifetimeTracked.self) { p in
autoreleasepool {
let object = LifetimeTracked(42)
p.pointee = object
expectEqual(LifetimeTracked.instances, originalInstances + 1)
}
// Draining the pool deallocates the instance, even though
// the autoreleasing pointer still points to it.
// This is okay as long as the pointer isn't dereferenced.
expectEqual(LifetimeTracked.instances, originalInstances)
}
expectEqual(LifetimeTracked.instances, originalInstances)
}
suite.test("subscript[0] is the same as pointee.getter") {
withAUMP(as: LifetimeTracked.self) { p in
expectNil(p[0])
}
let unmanaged = Unmanaged.passRetained(LifetimeTracked(42))
withAUMP(initialValue: unmanaged) { p in
expectTrue(p[0] === unmanaged.takeUnretainedValue())
}
unmanaged.release()
}
suite.test("subscript and advanced(by:) works") {
let originalInstances = LifetimeTracked.instances
let count = 100
var refs = 0
let values: [Unmanaged<LifetimeTracked>?] = (0 ..< count).map { i in
if i % 10 == 0 {
refs += 1
return Unmanaged.passRetained(LifetimeTracked(i))
} else {
return nil
}
}
withAUMP(initialValues: values) { p in
for i in 0 ..< count {
expectTrue(p[i] === values[i]?.takeUnretainedValue())
expectTrue(p.advanced(by: i).pointee === values[i]?.takeUnretainedValue())
}
}
expectEqual(LifetimeTracked.instances, originalInstances + refs)
for unmanaged in values {
unmanaged?.release()
}
expectEqual(LifetimeTracked.instances, originalInstances)
}
runAllTests()
|