Files
swift-mirror/test/1_stdlib/NewArray.swift.gyb
Dave Abrahams 4bdb9462c7 [stdlib] ArrayBufferType: identity is base address
Buffer identity is only used by tests.  The switch to an identity
representation that accounts for the buffer length was actually
incorrect for the way many tests used it.

Swift SVN r22771
2014-10-15 20:27:57 +00:00

508 lines
13 KiB
Swift

%# -*- mode: swift -*-
//===--- Array.swift ------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// RUN-DISABLED: %target-run-simple-swift | FileCheck %s
// RUN: rm -rf %t && mkdir -p %t && %S/../../utils/gyb %s -o %t/NewArray.swift
// RUN: %S/../../utils/line-directive %t/NewArray.swift -- %target-build-swift -module-cache-path %t/clang-module-cache %t/NewArray.swift -o %t/a.out -Xfrontend -disable-access-control
// RUN: %target-run %t/a.out | %S/../../utils/line-directive %t/NewArray.swift -- FileCheck %t/NewArray.swift
var xCount = 0
var xSerial = 0
import StdlibUnittest
// Instead of testing with Int elements, we use this wrapper class
// that can help us track allocations and find issues with object
// lifetime inside Array implementations.
final class X : ForwardIndexType, Comparable, Printable,
IntegerLiteralConvertible
{
required init(_ value: Int) {
++xCount
serial = ++xSerial
self.value = value
}
deinit {
assert(serial > 0, "double destruction!")
--xCount
serial = -serial
}
var description: String {
assert(serial > 0, "dead X!")
return value.description
}
func successor() -> Self {
return self.dynamicType(self.value.successor())
}
convenience init(integerLiteral value: Int) {
self.init(value)
}
var value: Int
var serial: Int
}
func == (x: X, y: X) -> Bool {
return x.value == y.value
}
func < (x: X, y: X) -> Bool {
return x.value < y.value
}
//===----------------------------------------------------------------------===//
func printSequence<T: SequenceType>(x: T) {
print("[")
var prefix = ""
for a in x {
print(prefix)
print(a)
prefix = ", "
}
println("]")
}
typealias BufferID = UnsafePointer<Void>
func bufferID<T : ArrayType>(x: T) -> BufferID {
return x._buffer.identity
}
func checkReallocation<T : ArrayType>(
x: T, lastBuffer: BufferID, reallocationExpected: Bool
) -> BufferID {
let currentBuffer = bufferID(x)
if (currentBuffer != lastBuffer) != reallocationExpected {
let message = reallocationExpected ? "lack of" : ""
println("unexpected \(message) reallocation")
}
return currentBuffer
}
func checkEqual<
S1 : SequenceType, S2 : SequenceType
where
S1.Generator.Element == S2.Generator.Element,
S1.Generator.Element : Equatable
>(a1: S1, a2: S2, expected: Bool) {
if equal(a1, a2) != expected {
let un = expected ? "un" : ""
println("unexpectedly \(un)equal sequences!")
}
}
func test<
T: ArrayType
where T.Generator.Element == T._Buffer.Element,
T._Buffer.Element == T.Element,
T.Element == X,
T.Index == Int
>(_: T.Type, label: String) {
print("test: \(label)...")
var x: T = [1, 2, 3, 4, 5]
checkEqual(x, 1...5, true)
x.reserveCapacity(x.count + 2)
checkEqual(x, 1...5, true)
let bufferId0 = bufferID(x)
// Append a range of integers
x += 0..<2
let bufferId1 = checkReallocation(x, bufferId0, false)
for i in x.count..<(x.capacity + 1) {
let bufferId1a = checkReallocation(x, bufferId1, false)
x.append(13)
}
let bufferId2 = checkReallocation(x, bufferId1, true)
let y = x
x[x.endIndex.predecessor()] = 17
let bufferId3 = checkReallocation(x, bufferId2, true)
checkEqual(x, y, false)
func checkReallocations(
var a: T, growthDescription: String, growBy1: (inout _: T)->()
) -> () {
var reallocations = 0
// Note: right now this test is dependent on a growth factor of 2.
// It's possible that factor will change, but (cursory) testing
// has shown that using 1.5, the other popular growth factor,
// slows things down.
for _ in a.count..<(a.capacity * 4) {
let oldId = bufferID(a)
growBy1(&a)
if oldId != bufferID(a) {
++reallocations
}
}
if reallocations > 3 {
println(
"Unexpectedly found \(reallocations) reallocations "
+ "of \(label) when growing via \(growthDescription)")
}
}
checkReallocations(x, "append") { (inout x: T)->() in x.append(42) }
checkReallocations(x, "+=") { (inout x: T)->() in x.append(42) }
println("done.")
}
println("testing...")
// CHECK: testing...
test(ContiguousArray<X>.self, "ContiguousArray")
// CHECK-NEXT: test: ContiguousArray...done
test(Array<X>.self, "Array")
// CHECK-NEXT: test: Array...done
test(Slice<X>.self, "Slice")
// CHECK-NEXT: test: Slice...done
func testAsArray() {
println("== AsArray ==")
var w: ContiguousArray<X> = [4, 2, 1]
// CHECK: == AsArray ==
let x = ContiguousArray(w)
println(bufferID(w) == bufferID(x))
// CHECK-NEXT: true
let y = Array(x)
println(bufferID(x) == bufferID(y))
// CHECK-NEXT: true
// Because of their indirection, arrays of classes can share
// buffers.
let y1 = Array(y)
println(bufferID(y1) == bufferID(y))
// CHECK-NEXT: true
let z = Slice(y)
println(bufferID(y) == bufferID(z))
// CHECK-NEXT: true
w = ContiguousArray(z)
println(bufferID(w) == bufferID(z))
// CHECK-NEXT: true
let zz = y[0..<2]
println(bufferID(zz))
}
testAsArray()
import Foundation
func nsArrayOfStrings() -> Array<NSString> {
let src: ContiguousArray<NSString> = ["foo", "bar", "baz"]
return src.withUnsafeBufferPointer {
let ns = NSArray(objects: UnsafePointer($0.baseAddress), count: $0.count)
return _convertNSArrayToArray(ns)
}
}
func testCocoa() {
println("== Cocoa ==")
// CHECK: == Cocoa ==
var a = nsArrayOfStrings()
printSequence(a)
// CHECK-NEXT: [foo, bar, baz]
a.append("qux")
printSequence(a)
// CHECK-NEXT: [foo, bar, baz, qux]
a = nsArrayOfStrings()
printSequence(a)
// CHECK-NEXT: [foo, bar, baz]
var b = a
a[1] = "garply"
printSequence(a)
// CHECK-NEXT: [foo, garply, baz]
// Mutating an element in a has no effect on b
printSequence(b)
// CHECK-NEXT: [foo, bar, baz]
a = nsArrayOfStrings()
a.insert("bag", atIndex:2)
printSequence(a)
// CHECK-NEXT: [foo, bar, bag, baz]
a = nsArrayOfStrings()
a.reserveCapacity(30)
printSequence(a)
// CHECK-NEXT: [foo, bar, baz]
println(a.capacity >= 30)
// CHECK-NEXT: true
// Prove that we create contiguous storage for an opaque NSArray
a.withUnsafeBufferPointer {
(p)->() in
println(p[0])
// CHECK-NEXT: foo
}
}
testCocoa()
extension Slice {
mutating func qsort(compare: (T,T)->Bool) {
_introSort(&self, indices(self), compare)
}
}
func testSlice() {
println("== Slice ==")
// CHECK: == Slice ==
// do some tests on the shared semantics
var b = ContiguousArray(X(0)..<X(7))
// Slice it
var bSlice = b[3..<5]
println("<\(bSlice.count)>")
// CHECK-NEXT: <2>
println("bSlice0: \(bSlice)") // CHECK-NEXT: bSlice0: [3, 4]
// bSlice += X(11)..<X(13)
// Writing into b does not change bSlice
b[4] = 41
println("bSlice1: \(bSlice)") // CHECK-NEXT: bSlice1: [3, 4]
// Writing into bSlice does not change b
bSlice[1] = 42
println("bSlice2: \(bSlice)") // CHECK-NEXT: bSlice2: [3, 42]
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6]
var c = b
b[4..<b.count].qsort(<)
printSequence(b) // CHECK-NEXT: [0, 1, 2, 3, 5, 6, 41]
printSequence(c) // CHECK-NEXT: [0, 1, 2, 3, 41, 5, 6]
// Now a bridged slice
var a = Array<NSString>(
_ArrayBuffer(nsArrayOfStrings()._asCocoaArray()))
printSequence(a) // CHECK-NEXT: [foo, bar, baz]
var aSlice = a[1..<3] // CHECK-NEXT: [bar, baz]
printSequence(aSlice)
// Writing into aSlice works
aSlice[0] = "buzz" // CHECK-NEXT: [buzz, baz]
printSequence(aSlice)
// ...and doesn't affect a
printSequence(a) // CHECK-NEXT: [foo, bar, baz]
// Appending to aSlice works...
aSlice.append("fodder")
println("<\(aSlice.count)>") // CHECK-NEXT: <3>
printSequence(aSlice) // CHECK-NEXT: [buzz, baz, fodder]
// And doesn't change a
printSequence(a) // CHECK-NEXT: [foo, bar, baz]
}
testSlice()
//===--- sub-range replacement --------------------------------------------===//
// Size of the array on which we're going to test "replace."
// testing time grows roughly as the cube of this constant
let testWidth = 11
%for A in ['ContiguousArray', 'Array', 'Slice']:
func testReplace(make: ()->${A}<X>) {
checkRangeReplaceable(make, { X(100)..<X(100 + $0) })
}
func testReplace${A}(
makeOne: ()->${A}<X> = {
var x = ${A}<X>()
// make sure some - but not all - replacements will have to grow the buffer
x.reserveCapacity(testWidth * 3 / 2)
x += X(0)..<X(testWidth)
return x
}
) {
testReplace(makeOne)
// Create one that will not be uniquely-referenced so we can test
// the out-of-place code paths.
let r = makeOne()
testReplace({ r })
// This test should ensure r's retain isn't dropped before we start testing.
if (r.count != testWidth) {
println("something bad happened!")
}
}
println("testing subrange replacement in ${A}")
testReplace${A}()
%end
// Also test with a sub-slice of some larger buffer. The "trailing"
// case is interesting because when the buffer is uniquely referenced
// we can expect to expand the slice in-place
for (maxValue, label) in [(testWidth, "trailing"), (testWidth*2, "interior")] {
println("testing subrange replacement in \(label) Sub-Slice")
testReplaceSlice {
var a = ContiguousArray(X(-testWidth)..<X(maxValue))
a.reserveCapacity(a.count * 3 / 2)
return a[testWidth..<(2 * testWidth)]
}
}
// CHECK-NEXT: testing subrange replacement in ContiguousArray
// CHECK-NEXT: testing subrange replacement in Array
// CHECK-NEXT: testing subrange replacement in Slice
// CHECK-NEXT: testing subrange replacement in trailing Sub-Slice
// CHECK-NEXT: testing subrange replacement in interior Sub-Slice
//===--- inout violations -------------------------------------------------===//
// The user has to obey certain rules when things are passed via
// inout, but in those cases we only guarantee memory-safety, not
// coherent semantics. Let's try to force a memory-safety problem
// here. This crashes when withUnsafeMutableBufferPointer is not
// sufficiently careful.
func testInoutViolation() {
var a: [X] = [
X(10), X(8), X(6), X(4), X(2), X(0), X(9), X(7), X(5), X(3), X(1)
]
%for A in ['ContiguousArray', 'Array', 'Slice']:
if true {
var b = ${A}(a)
b.sort { x, y in
b.removeAll()
return x < y
}
}
%end
// An overload of sort for Arrays uses withUnsafeMutableBufferPointer,
// which disables bounds checks.
sort(&a) { x, y in
a = [] // Invalidate the whole array during sorting
return x < y
}
}
testInoutViolation()
//===--- single-element modifiers -----------------------------------------===//
%for A in ['ContiguousArray', 'Array', 'Slice']:
func testSingleElementModifiers${A}() {
println("testing ${A} single-argument modifiers")
// CHECK-NEXT: testing ${A} single-argument modifiers
var a = ${A}(X(0)..<10)
println(a.removeLast().value) // CHECK-NEXT: 9
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8]
a.insert(42, atIndex: 4)
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8]
println(a.removeAtIndex(2).value) // CHECK-NEXT: 2
printSequence(a) // CHECK-NEXT: [0, 1, 3, 42, 4, 5, 6, 7, 8]
}
testSingleElementModifiers${A}()
%end
//===--- isEmpty, first, last ---------------------------------------------===//
%for A in ['ContiguousArray', 'Array', 'Slice']:
func testIsEmptyFirstLast${A}() {
println("testing ${A} isEmpty, first, and last")
// CHECK-NEXT: testing ${A} isEmpty, first, and last
println(${A}<Int>().isEmpty) // CHECK-NEXT: true
println(${A}(42...42).isEmpty) // CHECK-NEXT: false
println("<\(${A}(3...42).first!)>") // CHECK-NEXT: <3>
println("<\(${A}(3...42).last!)>") // CHECK-NEXT: <42>
println("<\(${A}<Int>().first)>") // CHECK-NEXT: nil
println("<\(${A}<Int>().last)>") // CHECK-NEXT: nil
var a = ${A}(X(0)..<10)
println(a.removeLast().value) // CHECK-NEXT: 9
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 4, 5, 6, 7, 8]
a.insert(42, atIndex: 4)
printSequence(a) // CHECK-NEXT: [0, 1, 2, 3, 42, 4, 5, 6, 7, 8]
println(a.removeAtIndex(2).value) // CHECK-NEXT: 2
printSequence(a) // CHECK-NEXT: [0, 1, 3, 42, 4, 5, 6, 7, 8]
}
testIsEmptyFirstLast${A}()
%end
//===--- Regression Tests -------------------------------------------------===//
func rdar16958865() {
var a: [Int] = []
a += SequenceOf([ 42, 4242 ])
// CHECK-NEXT: [42, 4242]
println(a)
}
rdar16958865()
import SpriteKit
class Rdar16914909 : NSObject {
var basicColorSet = [SKColor]()
func doColorStuff() {
basicColorSet.append(SKColor.lightGrayColor())
println("appended")
}
}
Rdar16914909().doColorStuff()
// CHECK-NEXT: appended
// FIXME: should be "leaks = 0", if not for <rdar://problem/17892507>
println("leaks = \(xCount)")
// CHECK-NEXT: leaks =
// CHECK-NEXT: all done.
println("all done.")
// ${'Local Variables'}:
// eval: (read-only-mode 1)
// End: