Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
//
//===----------------------------------------------------------------------===//

#if canImport(TestSupport)
import TestSupport
#endif
import Testing

#if FOUNDATION_FRAMEWORK
@testable import Foundation
#else
#if canImport(FoundationEssentials)
@testable import FoundationEssentials
#else
@testable import Foundation
#endif

extension AttributedStringProtocol {
Expand All @@ -27,7 +25,8 @@ extension AttributedStringProtocol {
}

/// Tests for `AttributedString` to confirm expected CoW behavior
final class TestAttributedStringCOW: XCTestCase {
@Suite("AttributedString Copy on Write")
private struct AttributedStringCOWTests {

// MARK: - Utility Functions

Expand All @@ -38,32 +37,32 @@ final class TestAttributedStringCOW: XCTestCase {
return str
}

func assertCOWCopy(file: StaticString = #filePath, line: UInt = #line, _ operation: (inout AttributedString) -> Void) {
func assertCOWCopy(sourceLocation: SourceLocation = #_sourceLocation, _ operation: (inout AttributedString) -> Void) {
let str = createAttributedString()
var copy = str
operation(&copy)
XCTAssertNotEqual(str, copy, "Mutation operation did not copy when multiple references exist", file: file, line: line)
#expect(str != copy, "Mutation operation did not copy when multiple references exist", sourceLocation: sourceLocation)
}

func assertCOWCopyManual(file: StaticString = #filePath, line: UInt = #line, _ operation: (inout AttributedString) -> Void) {
func assertCOWCopyManual(sourceLocation: SourceLocation = #_sourceLocation, _ operation: (inout AttributedString) -> Void) {
var str = createAttributedString()
let gutsPtr = Unmanaged.passUnretained(str._guts)
operation(&str)
let newGutsPtr = Unmanaged.passUnretained(str._guts)
XCTAssertNotEqual(gutsPtr.toOpaque(), newGutsPtr.toOpaque(), "Mutation operation with manual copy did not perform copy", file: file, line: line)
#expect(gutsPtr.toOpaque() != newGutsPtr.toOpaque(), "Mutation operation with manual copy did not perform copy", sourceLocation: sourceLocation)
}

func assertCOWNoCopy(file: StaticString = #filePath, line: UInt = #line, _ operation: (inout AttributedString) -> Void) {
func assertCOWNoCopy(sourceLocation: SourceLocation = #_sourceLocation, _ operation: (inout AttributedString) -> Void) {
var str = createAttributedString()
let gutsPtr = Unmanaged.passUnretained(str._guts)
operation(&str)
let newGutsPtr = Unmanaged.passUnretained(str._guts)
XCTAssertEqual(gutsPtr.toOpaque(), newGutsPtr.toOpaque(), "Mutation operation copied when only one reference exists", file: file, line: line)
#expect(gutsPtr.toOpaque() == newGutsPtr.toOpaque(), "Mutation operation copied when only one reference exists", sourceLocation: sourceLocation)
}

func assertCOWBehavior(file: StaticString = #filePath, line: UInt = #line, _ operation: (inout AttributedString) -> Void) {
assertCOWCopy(file: file, line: line, operation)
assertCOWNoCopy(file: file, line: line, operation)
func assertCOWBehavior(sourceLocation: SourceLocation = #_sourceLocation, _ operation: (inout AttributedString) -> Void) {
assertCOWCopy(sourceLocation: sourceLocation, operation)
assertCOWNoCopy(sourceLocation: sourceLocation, operation)
}

func makeSubrange(_ str: AttributedString) -> Range<AttributedString.Index> {
Expand All @@ -76,21 +75,22 @@ final class TestAttributedStringCOW: XCTestCase {
return RangeSet([rangeA, rangeB])
}

lazy var container: AttributeContainer = {
let container: AttributeContainer = {
var container = AttributeContainer()
container.testInt = 2
return container
}()

lazy var containerB: AttributeContainer = {
let containerB: AttributeContainer = {
var container = AttributeContainer()
container.testBool = true
return container
}()

// MARK: - Tests

func testTopLevelType() {
@Test
func topLevelType() {
assertCOWBehavior { (str) in
str.setAttributes(container)
}
Expand Down Expand Up @@ -126,7 +126,8 @@ final class TestAttributedStringCOW: XCTestCase {
}
}

func testSubstring() {
@Test
func substring() {
assertCOWBehavior { (str) in
str[makeSubrange(str)].setAttributes(container)
}
Expand All @@ -147,7 +148,8 @@ final class TestAttributedStringCOW: XCTestCase {
}
}

func testDiscontiguousSubstring() {
@Test
func discontiguousSubstring() {
assertCOWBehavior { (str) in
str[makeSubranges(str)].setAttributes(container)
}
Expand All @@ -172,7 +174,8 @@ final class TestAttributedStringCOW: XCTestCase {
}
}

func testCharacters() {
@Test
func characters() {
let char: Character = "a"

assertCOWBehavior { (str) in
Expand All @@ -195,15 +198,17 @@ final class TestAttributedStringCOW: XCTestCase {
}
}

func testUnicodeScalars() {
@Test
func unicodeScalars() {
let scalar: UnicodeScalar = "a"

assertCOWBehavior { (str) in
str.unicodeScalars.replaceSubrange(makeSubrange(str), with: [scalar, scalar])
}
}

func testGenericProtocol() {
@Test
func genericProtocol() {
assertCOWBehavior {
$0.genericSetAttribute()
}
Expand All @@ -212,7 +217,8 @@ final class TestAttributedStringCOW: XCTestCase {
}
}

func testIndexTracking() {
@Test
func indexTracking() {
assertCOWBehavior {
_ = $0.transform(updating: $0.startIndex ..< $0.endIndex) {
$0.testInt = 2
Expand Down Expand Up @@ -243,7 +249,7 @@ final class TestAttributedStringCOW: XCTestCase {
storage = $0
}
}
XCTAssertNotEqual(storage, "")
#expect(storage != "")

// Ensure the same semantics hold even when the closure throws
storage = AttributedString()
Expand All @@ -255,6 +261,6 @@ final class TestAttributedStringCOW: XCTestCase {
throw CocoaError(.fileReadUnknown)
}
}
XCTAssertNotEqual(storage, "")
#expect(storage != "")
}
}
Loading
Loading