Migrate unit tests to Swift Testing

This commit is contained in:
1024jp 2024-06-11 09:04:44 +09:00
parent d7ec330ed4
commit dffbcfecf0
42 changed files with 1788 additions and 1808 deletions

View File

@ -27,6 +27,7 @@
- [trivial] Suppress display of the “Extracting” message on the navigation bar in instantaneous parsing.
- [trivial] Make names of code contributors in the About window selectable.
- [dev] Update the build environment to Xcode 16.
- [dev] Migrate all unit tests to Swift Testing.
- [dev] Migrate the navigation bar and the Snippets settings view to SwiftUI.

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2022 1024jp
// © 2022-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,14 +23,14 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class ArithmeticsTests: XCTestCase {
struct ArithmeticsTests {
func testDigits() {
@Test func digits() {
XCTAssertEqual(0.digits, [0])
XCTAssertEqual(1024.digits, [4, 2, 0, 1])
#expect(0.digits == [0])
#expect(1024.digits == [4, 2, 0, 1])
}
}

View File

@ -24,69 +24,64 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class BracePairTests: XCTestCase {
struct BracePairTests {
func testIndexFind() {
@Test func findIndex() {
let string = "if < foo < 🐕 > > else < >"
let pair = BracePair("<", ">")
XCTAssertEqual(string.indexOfBracePair(endIndex: string.index(14), pair: pair), string.index(3))
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(4), pair: pair), string.index(15))
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(2), pair: pair))
XCTAssertNil(string.indexOfBracePair(beginIndex: string.index(2), pair: .ltgt))
#expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair) == string.index(3))
#expect(string.indexOfBracePair(beginIndex: string.index(4), pair: pair) == string.index(15))
#expect(string.indexOfBracePair(endIndex: string.index(2), pair: pair) == nil)
#expect(string.indexOfBracePair(beginIndex: string.index(2), pair: .ltgt) == nil)
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(14), pair: pair, until: string.index(15)))
XCTAssertNil(string.indexOfBracePair(beginIndex: string.index(4), pair: pair, until: string.index(2)))
#expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair, until: string.index(15)) == nil)
#expect(string.indexOfBracePair(beginIndex: string.index(4), pair: pair, until: string.index(2)) == nil)
}
func testSamePair() {
@Test func samePair() {
let string = "if ' foo ' 🐕 ' ' else ' '"
let pair = BracePair("'", "'")
XCTAssertEqual(string.indexOfBracePair(endIndex: string.index(14), pair: pair), string.index(13))
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(4), pair: pair), string.index(9))
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(2), pair: pair))
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(2), pair: pair), string.index(3))
#expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair) == string.index(13))
#expect(string.indexOfBracePair(beginIndex: string.index(4), pair: pair) == string.index(9))
#expect(string.indexOfBracePair(endIndex: string.index(2), pair: pair) == nil)
#expect(string.indexOfBracePair(beginIndex: string.index(2), pair: pair) == string.index(3))
}
func testScanner() {
@Test func scan() {
let string = "def { foo {} | { bar } } "
let pairs = BracePair.braces
XCTAssertNil(string.rangeOfEnclosingBracePair(at: string.range(1..<2), candidates: pairs))
XCTAssertNil(string.rangeOfEnclosingBracePair(at: string.range(24..<24), candidates: pairs))
#expect(string.rangeOfEnclosingBracePair(at: string.range(1..<2), candidates: pairs) == nil)
#expect(string.rangeOfEnclosingBracePair(at: string.range(24..<24), candidates: pairs) == nil)
XCTAssertEqual(string.rangeOfEnclosingBracePair(at: string.range(13..<14), candidates: pairs), // = |
string.range(4..<24))
#expect(string.rangeOfEnclosingBracePair(at: string.range(13..<14), candidates: pairs) == string.range(4..<24)) // = |
XCTAssertEqual(string.rangeOfEnclosingBracePair(at: string.range(11..<11), candidates: pairs), // = {}
string.range(10..<12))
#expect(string.rangeOfEnclosingBracePair(at: string.range(11..<11), candidates: pairs) == string.range(10..<12)) // = {}
}
func testScannerWithEscape() {
@Test func scanWithEscape() {
let pairs = BracePair.braces
let string1 = #"foo (\() )"#
XCTAssertEqual(string1.rangeOfEnclosingBracePair(at: string1.range(7..<7), candidates: pairs),
string1.range(4..<8))
#expect(string1.rangeOfEnclosingBracePair(at: string1.range(7..<7), candidates: pairs) == string1.range(4..<8))
let string2 = #"foo (\\() )"#
XCTAssertEqual(string2.rangeOfEnclosingBracePair(at: string2.range(8..<8), candidates: pairs),
string2.range(7..<9))
#expect(string2.rangeOfEnclosingBracePair(at: string2.range(8..<8), candidates: pairs) == string2.range(7..<9))
let string3 = #"foo (\\\() )"#
XCTAssertEqual(string3.rangeOfEnclosingBracePair(at: string3.range(9..<9), candidates: pairs),
string3.range(4..<10))
#expect(string3.rangeOfEnclosingBracePair(at: string3.range(9..<9), candidates: pairs) == string3.range(4..<10))
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2015-2023 1024jp
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,131 +24,132 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class CharacterInfoTests: XCTestCase {
struct CharacterInfoTests {
// MARK: UTF32.CodeUnit Extension Tests
func testSingleSurrogate() {
@Test func singleSurrogate() {
let character: UTF32.CodeUnit = 0xD83D
XCTAssertEqual(character.unicodeName, "<lead surrogate-D83D>")
XCTAssertEqual(character.blockName, "High Surrogates")
#expect(character.unicodeName == "<lead surrogate-D83D>")
#expect(character.blockName == "High Surrogates")
XCTAssertNil(Unicode.Scalar(character))
#expect(Unicode.Scalar(character) == nil)
}
// MARK: - UnicodeCharacter Tests
func testSingleChar() {
@Test func singleChar() {
let unicode = Unicode.Scalar("")
XCTAssertEqual(unicode.codePoint, "U+3042")
XCTAssertFalse(unicode.isSurrogatePair)
XCTAssertNil(unicode.surrogateCodePoints)
XCTAssertEqual(unicode.name, "HIRAGANA LETTER A")
XCTAssertEqual(unicode.blockName, "Hiragana")
XCTAssertNotNil(unicode.localizedBlockName)
#expect(unicode.codePoint == "U+3042")
#expect(!unicode.isSurrogatePair)
#expect(unicode.surrogateCodePoints == nil)
#expect(unicode.name == "HIRAGANA LETTER A")
#expect(unicode.blockName == "Hiragana")
#expect(unicode.localizedBlockName != nil)
}
func testSurrogateEmoji() {
@Test func surrogateEmoji() {
let unicode = Unicode.Scalar("😀")
XCTAssertEqual(unicode.codePoint, "U+1F600")
XCTAssertTrue(unicode.isSurrogatePair)
XCTAssertEqual(unicode.surrogateCodePoints?.lead, "U+D83D")
XCTAssertEqual(unicode.surrogateCodePoints?.trail, "U+DE00")
XCTAssertEqual(unicode.name, "GRINNING FACE")
XCTAssertEqual(unicode.blockName, "Emoticons")
XCTAssertNotNil(unicode.localizedBlockName)
#expect(unicode.codePoint == "U+1F600")
#expect(unicode.isSurrogatePair)
#expect(unicode.surrogateCodePoints?.lead == "U+D83D")
#expect(unicode.surrogateCodePoints?.trail == "U+DE00")
#expect(unicode.name == "GRINNING FACE")
#expect(unicode.blockName == "Emoticons")
#expect(unicode.localizedBlockName != nil)
}
func testUnicodeBlockNameWithHyphen() {
@Test func unicodeBlockNameWithHyphen() {
let character = Unicode.Scalar("")
XCTAssertEqual(character.codePoint, "U+FDFD")
XCTAssertEqual(character.name, "ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM")
XCTAssertEqual(character.localizedBlockName, "Arabic Presentation Forms-A")
#expect(character.codePoint == "U+FDFD")
#expect(character.name == "ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM")
#expect(character.localizedBlockName == "Arabic Presentation Forms-A")
}
func testUnicodeControlPictures() throws {
@Test func unicodeControlPictures() throws {
// test NULL
let nullCharacter = try XCTUnwrap(Unicode.Scalar(0x0000))
let nullPictureCharacter = try XCTUnwrap(Unicode.Scalar(0x2400))
XCTAssertEqual(nullCharacter.name, "NULL")
XCTAssertEqual(nullPictureCharacter.name, "SYMBOL FOR NULL")
XCTAssertEqual(nullCharacter.pictureRepresentation, nullPictureCharacter)
let nullCharacter = try #require(Unicode.Scalar(0x0000))
let nullPictureCharacter = try #require(Unicode.Scalar(0x2400))
#expect(nullCharacter.name == "NULL")
#expect(nullPictureCharacter.name == "SYMBOL FOR NULL")
#expect(nullCharacter.pictureRepresentation == nullPictureCharacter)
// test SPACE
let spaceCharacter = try XCTUnwrap(Unicode.Scalar(0x0020))
let spacePictureCharacter = try XCTUnwrap(Unicode.Scalar(0x2420))
XCTAssertEqual(spaceCharacter.name, "SPACE")
XCTAssertEqual(spacePictureCharacter.name, "SYMBOL FOR SPACE")
XCTAssertEqual(spaceCharacter.pictureRepresentation, spacePictureCharacter)
let spaceCharacter = try #require(Unicode.Scalar(0x0020))
let spacePictureCharacter = try #require(Unicode.Scalar(0x2420))
#expect(spaceCharacter.name == "SPACE")
#expect(spacePictureCharacter.name == "SYMBOL FOR SPACE")
#expect(spaceCharacter.pictureRepresentation == spacePictureCharacter)
// test DELETE
let deleteCharacter = try XCTUnwrap(Unicode.Scalar(NSDeleteCharacter))
let deleteCharacter = try #require(Unicode.Scalar(NSDeleteCharacter))
let deletePictureCharacter = Unicode.Scalar("")
XCTAssertEqual(deleteCharacter.name, "DELETE")
XCTAssertEqual(deletePictureCharacter.name, "SYMBOL FOR DELETE")
XCTAssertEqual(deleteCharacter.pictureRepresentation, deletePictureCharacter)
#expect(deleteCharacter.name == "DELETE")
#expect(deletePictureCharacter.name == "SYMBOL FOR DELETE")
#expect(deleteCharacter.pictureRepresentation == deletePictureCharacter)
// test one after the last C0 control character
let exclamationCharacter = try XCTUnwrap(Unicode.Scalar(0x0021))
XCTAssertEqual(exclamationCharacter.name, "EXCLAMATION MARK")
XCTAssertNil(exclamationCharacter.pictureRepresentation)
let exclamationCharacter = try #require(Unicode.Scalar(0x0021))
#expect(exclamationCharacter.name == "EXCLAMATION MARK")
#expect(exclamationCharacter.pictureRepresentation == nil)
}
// MARK: - CharacterInfo Tests
func testSingleCharWithVSInfo() {
@Test func singleCharacterWithVSInfo() {
let charInfo = CharacterInfo(character: "☺︎")
XCTAssertEqual(charInfo.character, "☺︎")
XCTAssertFalse(charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+263A", "U+FE0E"])
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.name), ["WHITE SMILING FACE", "VARIATION SELECTOR-15"])
XCTAssertEqual(charInfo.localizedDescription, "WHITE SMILING FACE (Text Style)")
#expect(charInfo.character == "☺︎")
#expect(!charInfo.isComplex)
#expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+263A", "U+FE0E"])
#expect(charInfo.character.unicodeScalars.map(\.name) == ["WHITE SMILING FACE", "VARIATION SELECTOR-15"])
#expect(charInfo.localizedDescription == "WHITE SMILING FACE (Text Style)")
}
func testCombiningCharacterInfo() {
@Test func combiningCharacterInfo() {
let charInfo = CharacterInfo(character: "1")
XCTAssertTrue(charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+0031", "U+FE0F", "U+20E3"])
XCTAssertEqual(charInfo.localizedDescription, "<a letter consisting of 3 characters>")
#expect(charInfo.isComplex)
#expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+0031", "U+FE0F", "U+20E3"])
#expect(charInfo.localizedDescription == "<a letter consisting of 3 characters>")
}
func testNationalIndicatorInfo() {
@Test func nationalIndicatorInfo() {
let charInfo = CharacterInfo(character: "🇯🇵")
XCTAssertTrue(charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+1F1EF", "U+1F1F5"])
#expect(charInfo.isComplex)
#expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+1F1EF", "U+1F1F5"])
}
func testControlCharacterInfo() {
@Test func controlCharacterInfo() {
let charInfo = CharacterInfo(character: " ")
XCTAssertEqual(charInfo.character, " ")
XCTAssertEqual(charInfo.pictureCharacter, "")
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.name), ["SPACE"])
#expect(charInfo.character == " ")
#expect(charInfo.pictureCharacter == "")
#expect(charInfo.character.unicodeScalars.map(\.name) == ["SPACE"])
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2017-2022 1024jp
// © 2017-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,104 +24,100 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class CollectionTests: XCTestCase {
struct CollectionTests {
func testAppendUnique() {
@Test func appendUnique() {
var array = [0, 1, 2, 3, 4]
array.appendUnique(0, maximum: 5)
XCTAssertEqual(array, [1, 2, 3, 4, 0])
#expect(array == [1, 2, 3, 4, 0])
array.appendUnique(6, maximum: 5)
XCTAssertEqual(array, [2, 3, 4, 0, 6])
#expect(array == [2, 3, 4, 0, 6])
array.appendUnique(7, maximum: 6)
XCTAssertEqual(array, [2, 3, 4, 0, 6, 7])
#expect(array == [2, 3, 4, 0, 6, 7])
array.appendUnique(6, maximum: 3)
XCTAssertEqual(array, [0, 7, 6])
#expect(array == [0, 7, 6])
}
func testCount() {
@Test func count() {
XCTAssertEqual([1, 2, 0, -1, 3].count(where: { $0 > 0 }), 3)
XCTAssertEqual([0, 1, 2, 0, -1].count(where: { $0 > 0 }), 2)
XCTAssertEqual([1, 2, 3, 4, 5].count(where: { $0 > 0 }), 5)
#expect([1, 2, 0, -1, 3].count(where: { $0 > 0 }) == 3)
#expect([0, 1, 2, 0, -1].count(where: { $0 > 0 }) == 2)
#expect([1, 2, 3, 4, 5].count(where: { $0 > 0 }) == 5)
XCTAssertEqual([1, 2, 0, -1, 3].countPrefix(while: { $0 > 0 }), 2)
XCTAssertEqual([0, 1, 2, 0, -1].countPrefix(while: { $0 > 0 }), 0)
XCTAssertEqual([1, 2, 3, 4, 5].countPrefix(while: { $0 > 0 }), 5)
#expect([1, 2, 0, -1, 3].countPrefix(while: { $0 > 0 }) == 2)
#expect([0, 1, 2, 0, -1].countPrefix(while: { $0 > 0 }) == 0)
#expect([1, 2, 3, 4, 5].countPrefix(while: { $0 > 0 }) == 5)
}
func testCountComparison() {
@Test func compareCount() {
XCTAssertEqual("".compareCount(with: 0), .equal)
XCTAssertEqual("".compareCount(with: 1), .less)
#expect("".compareCount(with: 0) == .equal)
#expect("".compareCount(with: 1) == .less)
XCTAssertEqual("a".compareCount(with: 1), .equal)
XCTAssertEqual("🐕".compareCount(with: 1), .equal)
XCTAssertEqual("🐕‍🦺".compareCount(with: 1), .equal)
#expect("a".compareCount(with: 1) == .equal)
#expect("🐕".compareCount(with: 1) == .equal)
#expect("🐕‍🦺".compareCount(with: 1) == .equal)
XCTAssertEqual("🐶🐱".compareCount(with: 3), .less)
XCTAssertEqual("🐶🐱".compareCount(with: 2), .equal)
XCTAssertEqual("🐶🐱".compareCount(with: 1), .greater)
#expect("🐶🐱".compareCount(with: 3) == .less)
#expect("🐶🐱".compareCount(with: 2) == .equal)
#expect("🐶🐱".compareCount(with: 1) == .greater)
}
func testKeyMapping() {
@Test func mapKeys() {
let dict = [1: 1, 2: 2, 3: 3]
let mapped = dict.mapKeys { String($0 * 10) }
XCTAssertEqual(mapped, ["10": 1, "20": 2, "30": 3])
#expect(mapped == ["10": 1, "20": 2, "30": 3])
}
func testRawRepresentable() {
@Test func rawRepresentable() {
enum TestKey: String {
case dog, cat, cow
}
var dict = ["dog": "🐶", "cat": "🐱"]
XCTAssertEqual(dict[TestKey.dog], dict[TestKey.dog.rawValue])
XCTAssertNil(dict[TestKey.cow])
#expect(dict[TestKey.dog] == dict[TestKey.dog.rawValue])
#expect(dict[TestKey.cow] == nil)
dict[TestKey.cow] = "🐮"
XCTAssertEqual(dict[TestKey.cow], "🐮")
#expect(dict[TestKey.cow] == "🐮")
}
func testSorting() {
@Test(arguments: 0..<10) func sort(index: Int) {
for _ in 0..<10 {
var array: [Int] = (0..<10).map { _ in .random(in: 0..<100) }
let sorted = array.sorted { $0 < $1 }
XCTAssertEqual(array.sorted(), sorted)
#expect(array.sorted() == sorted)
array.sort()
XCTAssertEqual(array, sorted)
}
#expect(array == sorted)
}
func testBinarySearch() {
@Test(arguments: 0..<10) func binarySearch(index: Int) {
for _ in 0..<10 {
let array = (0..<20).map { _ in Int.random(in: 0..<100) }.sorted()
for _ in 0..<10 {
let index = Int.random(in: 0..<100)
XCTAssertEqual(array.binarySearchedFirstIndex(where: { $0 > index }),
#expect(array.binarySearchedFirstIndex(where: { $0 > index }) ==
array.firstIndex(where: { $0 > index }))
}
}
}
}

View File

@ -23,26 +23,27 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class ComparableTests: XCTestCase {
struct ComparableTests {
func testClamp() {
@Test func clamp() {
XCTAssertEqual((-2).clamped(to: -10...10), -2)
XCTAssertEqual(5.clamped(to: 6...10), 6)
XCTAssertEqual(20.clamped(to: 6...10), 10)
#expect((-2).clamped(to: -10...10) == -2)
#expect(5.clamped(to: 6...10) == 6)
#expect(20.clamped(to: 6...10) == 10)
}
func testBoolComparison() {
@Test func compareBool() {
XCTAssertEqual([false, true, false, true, false].sorted(), [true, true, false, false, false])
#expect([false, true, false, true, false].sorted() == [true, true, false, false, false])
}
func testBoolItemComparison() {
@Test func compareBoolItem() {
struct Item: Equatable {
@ -58,7 +59,7 @@ final class ComparableTests: XCTestCase {
Item(id: 4, bool: true),
]
XCTAssertEqual(items.sorted(\.bool), [
#expect(items.sorted(\.bool) == [
Item(id: 1, bool: true),
Item(id: 2, bool: true),
Item(id: 4, bool: true),
@ -66,7 +67,7 @@ final class ComparableTests: XCTestCase {
Item(id: 3, bool: false),
])
XCTAssertEqual(items.sorted(using: [KeyPathComparator(\.bool)]), [
#expect(items.sorted(using: [KeyPathComparator(\.bool)]) == [
Item(id: 1, bool: true),
Item(id: 2, bool: true),
Item(id: 4, bool: true),

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2020 1024jp
// © 2020-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,71 +23,55 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class DebouncerTests: XCTestCase {
struct DebouncerTests {
func testDebounce() {
@Test func debounce() async throws {
let expectation = self.expectation(description: "Debouncer executed")
let waitingExpectation = self.expectation(description: "Debouncer waiting")
waitingExpectation.isInverted = true
var value = 0
try await confirmation("Debouncer executed", expectedCount: 1) { confirm in
let debouncer = Debouncer(delay: .seconds(0.5)) {
value += 1
expectation.fulfill()
waitingExpectation.fulfill()
confirm()
}
XCTAssertEqual(value, 0)
debouncer.schedule()
debouncer.schedule()
self.wait(for: [waitingExpectation], timeout: 0.1)
XCTAssertEqual(value, 0)
self.wait(for: [expectation], timeout: 0.5)
XCTAssertEqual(value, 1)
try await Task.sleep(for: .seconds(1))
}
}
func testImidiateFire() {
@Test func immediateFire() {
var value = 0
let debouncer = Debouncer {
value += 1
}
XCTAssertEqual(0, value)
#expect(0 == value)
debouncer.fireNow()
XCTAssertEqual(value, 0, "The action is performed only when scheduled.")
#expect(value == 0, "The action is performed only when scheduled.")
debouncer.schedule()
XCTAssertEqual(value, 0)
#expect(value == 0)
debouncer.fireNow()
XCTAssertEqual(value, 1, "The scheduled action must be performed immediately.")
#expect(value == 1, "The scheduled action must be performed immediately.")
}
func testCancellation() {
let expectation = self.expectation(description: "Debouncer cancelled")
expectation.isInverted = true
@Test func cancel() async {
await confirmation("Debouncer cancelled", expectedCount: 0) { confirm in
let debouncer = Debouncer {
expectation.fulfill()
confirm()
}
debouncer.schedule()
debouncer.cancel()
self.waitForExpectations(timeout: 1)
}
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2023 1024jp
// © 2023-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,13 +23,14 @@
// limitations under the License.
//
import AppKit
import Combine
import XCTest
import Testing
@testable import CotEditor
final class EditedRangeSetTests: XCTestCase {
struct EditedRangeSetTests {
func testRangeSet() throws {
@Test func rangeSet() throws {
// abcdefg
var set = EditedRangeSet()
@ -37,35 +38,35 @@ final class EditedRangeSetTests: XCTestCase {
// ab|0000|efg
// .replaceCharacters(in: NSRange(2..<3), with: "0000")
set.append(editedRange: NSRange(location: 2, length: 4), changeInLength: 2)
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 4)])
#expect(set.ranges == [NSRange(location: 2, length: 4)])
// ab0000e|g
// .replaceCharacters(in: NSRange(7..<8), with: "")
set.append(editedRange: NSRange(location: 7, length: 0), changeInLength: -1)
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 4),
#expect(set.ranges == [NSRange(location: 2, length: 4),
NSRange(location: 7, length: 0)])
// ab0|0eg
// .replaceCharacters(in: NSRange(3..<5), with: "")
set.append(editedRange: NSRange(location: 3, length: 0), changeInLength: -2)
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 2),
#expect(set.ranges == [NSRange(location: 2, length: 2),
NSRange(location: 5, length: 0)])
// a|1|b00eg
// .replaceCharacters(in: NSRange(1..<1), with: "1")
set.append(editedRange: NSRange(location: 1, length: 1), changeInLength: 1)
XCTAssertEqual(set.ranges, [NSRange(location: 1, length: 1),
#expect(set.ranges == [NSRange(location: 1, length: 1),
NSRange(location: 3, length: 2),
NSRange(location: 6, length: 0)])
set.clear()
XCTAssert(set.ranges.isEmpty)
#expect(set.ranges.isEmpty)
}
func testUnion() throws {
@Test func union() throws {
XCTAssertEqual(NSRange(2..<3).union(NSRange(3..<4)), NSRange(2..<4))
#expect(NSRange(2..<3).union(NSRange(3..<4)) == NSRange(2..<4))
let textStorage = NSTextStorage("abcdefghij")
var set = EditedRangeSet()
@ -74,22 +75,22 @@ final class EditedRangeSetTests: XCTestCase {
set.append(editedRange: NSRange(location: 2, length: 2), changeInLength: 0)
textStorage.replaceCharacters(in: NSRange(location: 6, length: 2), with: "00")
set.append(editedRange: NSRange(location: 6, length: 2), changeInLength: 0)
XCTAssertEqual(textStorage.string, "ab00ef00ij")
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 2), NSRange(location: 6, length: 2)])
#expect(textStorage.string == "ab00ef00ij")
#expect(set.ranges == [NSRange(location: 2, length: 2), NSRange(location: 6, length: 2)])
textStorage.replaceCharacters(in: NSRange(location: 3, length: 4), with: "11")
set.append(editedRange: NSRange(location: 3, length: 2), changeInLength: -2)
XCTAssertEqual(textStorage.string, "ab0110ij")
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 4)])
#expect(textStorage.string == "ab0110ij")
#expect(set.ranges == [NSRange(location: 2, length: 4)])
textStorage.replaceCharacters(in: NSRange(location: 1, length: 3), with: "22")
set.append(editedRange: NSRange(location: 1, length: 2), changeInLength: -1)
XCTAssertEqual(textStorage.string, "a2210ij")
XCTAssertEqual(set.ranges, [NSRange(location: 1, length: 4)])
#expect(textStorage.string == "a2210ij")
#expect(set.ranges == [NSRange(location: 1, length: 4)])
}
func testJoin() throws {
@Test func join() throws {
var set = EditedRangeSet()
@ -98,24 +99,22 @@ final class EditedRangeSetTests: XCTestCase {
set.append(editedRange: NSRange(location: 0, length: 2), changeInLength: 0)
set.append(editedRange: NSRange(location: 2, length: 2), changeInLength: 0)
XCTAssertEqual(set.ranges, [NSRange(location: 0, length: 6)])
#expect(set.ranges == [NSRange(location: 0, length: 6)])
}
func testStorageTest() async throws {
@Test func testStorage() async throws {
let textStorage = NSTextStorage("abcdefg")
var set = EditedRangeSet()
let expectation = self.expectation(description: "UserDefaults observation for normal key")
expectation.expectedFulfillmentCount = 4
await confirmation("UserDefaults observation for normal key", expectedCount: 4) { confirm in
let observer = NotificationCenter.default.publisher(for: NSTextStorage.didProcessEditingNotification, object: textStorage)
.map { $0.object as! NSTextStorage }
.filter { $0.editedMask.contains(.editedCharacters) }
.sink { storage in
set.append(editedRange: storage.editedRange, changeInLength: storage.changeInLength)
expectation.fulfill()
confirm()
}
textStorage.replaceCharacters(in: NSRange(2..<4), with: "0000")
@ -123,13 +122,12 @@ final class EditedRangeSetTests: XCTestCase {
textStorage.replaceCharacters(in: NSRange(3..<5), with: "")
textStorage.replaceCharacters(in: NSRange(1..<1), with: "1")
await self.fulfillment(of: [expectation], timeout: 2)
XCTAssertEqual(textStorage.string, "a1b00eg")
XCTAssertEqual(set.ranges, [NSRange(location: 1, length: 1),
#expect(textStorage.string == "a1b00eg")
#expect(set.ranges == [NSRange(location: 1, length: 1),
NSRange(location: 3, length: 2),
NSRange(location: 6, length: 0)])
observer.cancel()
}
}
}

View File

@ -23,10 +23,11 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class EditorCounterTests: XCTestCase {
final class EditorCounterTests {
@MainActor final class Provider: TextViewProvider {
@ -47,7 +48,7 @@ final class EditorCounterTests: XCTestCase {
Both are 👍🏼.
"""
@MainActor func testNoRequiredInfo() throws {
@MainActor @Test func noRequiredInfo() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(0..<3))
@ -56,16 +57,16 @@ final class EditorCounterTests: XCTestCase {
counter.invalidateContent()
counter.invalidateSelection()
XCTAssertNil(counter.result.lines.entire)
XCTAssertNil(counter.result.characters.entire)
XCTAssertNil(counter.result.words.entire)
XCTAssertNil(counter.result.location)
XCTAssertNil(counter.result.line)
XCTAssertNil(counter.result.column)
#expect(counter.result.lines.entire == nil)
#expect(counter.result.characters.entire == nil)
#expect(counter.result.words.entire == nil)
#expect(counter.result.location == nil)
#expect(counter.result.line == nil)
#expect(counter.result.column == nil)
}
@MainActor func testAllRequiredInfo() throws {
@MainActor @Test func allRequiredInfo() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(11..<21))
@ -75,21 +76,21 @@ final class EditorCounterTests: XCTestCase {
counter.invalidateContent()
counter.invalidateSelection()
// XCTAssertEqual(counter.result.lines.entire, 3)
// XCTAssertEqual(counter.result.characters.entire, 31)
// XCTAssertEqual(counter.result.words.entire, 6)
// #expect(counter.result.lines.entire == 3)
// #expect(counter.result.characters.entire == 31)
// #expect(counter.result.words.entire == 6)
// XCTAssertEqual(counter.result.characters.selected, 9)
// XCTAssertEqual(counter.result.lines.selected, 1)
// XCTAssertEqual(counter.result.words.selected, 2)
// #expect(counter.result.characters.selected == 9)
// #expect(counter.result.lines.selected == 1)
// #expect(counter.result.words.selected == 2)
// XCTAssertEqual(counter.result.location, 10)
// XCTAssertEqual(counter.result.column, 0)
// XCTAssertEqual(counter.result.line, 2)
// #expect(counter.result.location == 10)
// #expect(counter.result.column == 0)
// #expect(counter.result.line == 2)
}
@MainActor func testWholeTextSkip() throws {
@MainActor @Test func skipWholeText() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(11..<21))
@ -98,21 +99,21 @@ final class EditorCounterTests: XCTestCase {
counter.updatesAll = true
counter.invalidateSelection()
XCTAssertNil(counter.result.lines.entire)
XCTAssertNil(counter.result.characters.entire)
XCTAssertNil(counter.result.words.entire)
#expect(counter.result.lines.entire == nil)
#expect(counter.result.characters.entire == nil)
#expect(counter.result.words.entire == nil)
// XCTAssertEqual(counter.result.lines.selected, 1)
// XCTAssertEqual(counter.result.characters.selected, 9)
// XCTAssertEqual(counter.result.words.selected, 2)
// #expect(counter.result.lines.selected == 1)
// #expect(counter.result.characters.selected == 9)
// #expect(counter.result.words.selected == 2)
// XCTAssertEqual(counter.result.location, 10)
// XCTAssertEqual(counter.result.column, 0)
// XCTAssertEqual(counter.result.line, 2)
// #expect(counter.result.location == 10)
// #expect(counter.result.column == 0)
// #expect(counter.result.line == 2)
}
@MainActor func testCRLF() throws {
@MainActor @Test func crlf() throws {
let provider = Provider(string: "a\r\nb", selectedRange: NSRange(1..<4))
@ -122,33 +123,33 @@ final class EditorCounterTests: XCTestCase {
counter.invalidateContent()
counter.invalidateSelection()
// XCTAssertEqual(counter.result.lines.entire, 2)
// XCTAssertEqual(counter.result.characters.entire, 3)
// XCTAssertEqual(counter.result.words.entire, 2)
// #expect(counter.result.lines.entire == 2)
// #expect(counter.result.characters.entire == 3)
// #expect(counter.result.words.entire == 2)
// XCTAssertEqual(counter.result.lines.selected, 2)
// XCTAssertEqual(counter.result.characters.selected, 2)
// XCTAssertEqual(counter.result.words.selected, 1)
// #expect(counter.result.lines.selected == 2)
// #expect(counter.result.characters.selected == 2)
// #expect(counter.result.words.selected == 1)
// XCTAssertEqual(counter.result.location, 1)
// XCTAssertEqual(counter.result.column, 1)
// XCTAssertEqual(counter.result.line, 1)
// #expect(counter.result.location == 1)
// #expect(counter.result.column == 1)
// #expect(counter.result.line == 1)
}
func testEditorCountFormatting() {
@Test func formatEditorCount() {
var count = EditorCount()
XCTAssertNil(count.formatted)
#expect(count.formatted == nil)
count.entire = 1000
XCTAssertEqual(count.formatted, "1,000")
#expect(count.formatted == "1,000")
count.selected = 100
XCTAssertEqual(count.formatted, "1,000 (100)")
#expect(count.formatted == "1,000 (100)")
count.entire = nil
XCTAssertNil(count.formatted)
#expect(count.formatted == nil)
}
}

View File

@ -24,54 +24,52 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class EncodingDetectionTests: XCTestCase {
final class EncodingDetectionTests {
private lazy var bundle = Bundle(for: type(of: self))
func testUTF8BOM() throws {
@Test func utf8BOM() throws {
// -> String(data:encoding:) preserves BOM since Swift 5 (2019-03)
// cf. https://bugs.swift.org/browse/SR-10173
let data = try self.dataForFileName("UTF-8 BOM")
XCTAssertEqual(String(decoding: data, as: UTF8.self), "\u{FEFF}0")
XCTAssertEqual(String(bomCapableData: data, encoding: .utf8), "0")
#expect(String(decoding: data, as: UTF8.self) == "\u{FEFF}0")
#expect(String(bomCapableData: data, encoding: .utf8) == "0")
var encoding: String.Encoding?
let string = try self.encodedStringForFileName("UTF-8 BOM", usedEncoding: &encoding)
XCTAssertEqual(string, "0")
XCTAssertEqual(encoding, .utf8)
#expect(string == "0")
#expect(encoding == .utf8)
XCTAssertEqual(String(bomCapableData: Data(Unicode.BOM.utf8.sequence), encoding: .utf8), "")
XCTAssertEqual(String(bomCapableData: Data(), encoding: .utf8), "")
#expect(String(bomCapableData: Data(Unicode.BOM.utf8.sequence), encoding: .utf8)?.isEmpty == true)
#expect(String(bomCapableData: Data(), encoding: .utf8)?.isEmpty == true)
}
func testUTF16() throws {
@Test func utf16() throws {
var encoding: String.Encoding?
let string = try self.encodedStringForFileName("UTF-16", usedEncoding: &encoding)
XCTAssertEqual(string, "0")
XCTAssertEqual(encoding, .utf16)
#expect(string == "0")
#expect(encoding == .utf16)
}
func testUTF32() throws {
@Test func utf32() throws {
var encoding: String.Encoding?
let string = try self.encodedStringForFileName("UTF-32", usedEncoding: &encoding)
XCTAssertEqual(string, "0")
XCTAssertEqual(encoding, .utf32)
#expect(string == "0")
#expect(encoding == .utf32)
}
func testISO2022() throws {
@Test func iso2022() throws {
let data = try self.dataForFileName("ISO 2022-JP")
let encodings: [String.Encoding] = [.iso2022JP, .utf16]
@ -79,24 +77,24 @@ final class EncodingDetectionTests: XCTestCase {
var encoding: String.Encoding?
let string = try String(data: data, suggestedEncodings: encodings, usedEncoding: &encoding)
XCTAssertEqual(string, "dog犬")
XCTAssertEqual(encoding, .iso2022JP)
#expect(string == "dog犬")
#expect(encoding == .iso2022JP)
}
func testUTF8() throws {
@Test func utf8() throws {
let data = try self.dataForFileName("UTF-8")
var encoding: String.Encoding?
XCTAssertThrowsError(try String(data: data, suggestedEncodings: [], usedEncoding: &encoding)) { error in
XCTAssertEqual(error as? CocoaError, CocoaError(.fileReadUnknownStringEncoding))
#expect(throws: CocoaError(.fileReadUnknownStringEncoding)) {
try String(data: data, suggestedEncodings: [], usedEncoding: &encoding)
}
XCTAssertNil(encoding)
#expect(encoding == nil)
}
func testSuggestedEncoding() throws {
@Test func suggestedEncoding() throws {
let data = try self.dataForFileName("UTF-8")
@ -104,66 +102,66 @@ final class EncodingDetectionTests: XCTestCase {
let invalidEncoding = String.Encoding(cfEncoding: kCFStringEncodingInvalidId)
let string = try String(data: data, suggestedEncodings: [invalidEncoding, .utf8], usedEncoding: &encoding)
XCTAssertEqual(string, "0")
XCTAssertEqual(encoding, .utf8)
#expect(string == "0")
#expect(encoding == .utf8)
}
func testEmptyData() {
@Test func emptyData() {
let data = Data()
var encoding: String.Encoding?
var string: String?
XCTAssertThrowsError(string = try String(data: data, suggestedEncodings: [], usedEncoding: &encoding)) { error in
XCTAssertEqual(error as? CocoaError, CocoaError(.fileReadUnknownStringEncoding))
#expect(throws: CocoaError(.fileReadUnknownStringEncoding)) {
string = try String(data: data, suggestedEncodings: [], usedEncoding: &encoding)
}
XCTAssertNil(string)
XCTAssertNil(encoding)
XCTAssertFalse(data.starts(with: Unicode.BOM.utf8.sequence))
#expect(string == nil)
#expect(encoding == nil)
#expect(!data.starts(with: Unicode.BOM.utf8.sequence))
}
func testUTF8BOMData() throws {
@Test func utf8BOMData() throws {
let withBOMData = try self.dataForFileName("UTF-8 BOM")
XCTAssertTrue(withBOMData.starts(with: Unicode.BOM.utf8.sequence))
#expect(withBOMData.starts(with: Unicode.BOM.utf8.sequence))
let data = try self.dataForFileName("UTF-8")
XCTAssertFalse(data.starts(with: Unicode.BOM.utf8.sequence))
#expect(!data.starts(with: Unicode.BOM.utf8.sequence))
}
func testEncodingDeclarationScan() {
@Test func scanEncodingDeclaration() throws {
let string = "<meta charset=\"Shift_JIS\"/>"
XCTAssertNil(string.scanEncodingDeclaration(upTo: 16))
XCTAssertEqual(string.scanEncodingDeclaration(upTo: 128), String.Encoding(cfEncodings: .shiftJIS))
#expect(string.scanEncodingDeclaration(upTo: 16) == nil)
#expect(string.scanEncodingDeclaration(upTo: 128) == String.Encoding(cfEncodings: .shiftJIS))
XCTAssertEqual("<meta charset=\"utf-8\"/>".scanEncodingDeclaration(upTo: 128), .utf8)
#expect("<meta charset=\"utf-8\"/>".scanEncodingDeclaration(upTo: 128) == .utf8)
// Swift.Regex with non-simple word boundaries never returns when the given string contains a specific pattern of letters (2023-12 on Swift 5.9).
XCTAssertNil("タマゴ,1,".scanEncodingDeclaration(upTo: 128))
XCTAssertNil(try /\ba/.wordBoundaryKind(.simple).firstMatch(in: "タマゴ,1,"))
#expect("タマゴ,1,".scanEncodingDeclaration(upTo: 128) == nil)
#expect(try /\ba/.wordBoundaryKind(.simple).firstMatch(in: "タマゴ,1,") == nil)
}
func testEncodingInitialization() {
@Test func initializeEncoding() {
XCTAssertEqual(String.Encoding(cfEncodings: CFStringEncodings.dosJapanese), .shiftJIS)
XCTAssertNotEqual(String.Encoding(cfEncodings: CFStringEncodings.shiftJIS), .shiftJIS)
XCTAssertNotEqual(String.Encoding(cfEncodings: CFStringEncodings.shiftJIS_X0213), .shiftJIS)
#expect(String.Encoding(cfEncodings: CFStringEncodings.dosJapanese) == .shiftJIS)
#expect(String.Encoding(cfEncodings: CFStringEncodings.shiftJIS) != .shiftJIS)
#expect(String.Encoding(cfEncodings: CFStringEncodings.shiftJIS_X0213) != .shiftJIS)
XCTAssertEqual(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.dosJapanese.rawValue)), .shiftJIS)
XCTAssertNotEqual(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.shiftJIS.rawValue)), .shiftJIS)
XCTAssertNotEqual(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.shiftJIS_X0213.rawValue)), .shiftJIS)
#expect(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.dosJapanese.rawValue)) == .shiftJIS)
#expect(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.shiftJIS.rawValue)) != .shiftJIS)
#expect(String.Encoding(cfEncoding: CFStringEncoding(CFStringEncodings.shiftJIS_X0213.rawValue)) != .shiftJIS)
}
/// Makes sure the behaviors around Shift-JIS.
func testShiftJIS() {
@Test func shiftJIS() {
let shiftJIS = CFStringEncoding(CFStringEncodings.shiftJIS.rawValue)
let shiftJIS_X0213 = CFStringEncoding(CFStringEncodings.shiftJIS_X0213.rawValue)
@ -171,68 +169,68 @@ final class EncodingDetectionTests: XCTestCase {
// IANA charset name conversion
// CFStringEncoding -> IANA charset name
XCTAssertEqual(CFStringConvertEncodingToIANACharSetName(shiftJIS) as String, "shift_jis")
XCTAssertEqual(CFStringConvertEncodingToIANACharSetName(shiftJIS_X0213) as String, "Shift_JIS")
#expect(CFStringConvertEncodingToIANACharSetName(shiftJIS) as String == "shift_jis")
#expect(CFStringConvertEncodingToIANACharSetName(shiftJIS_X0213) as String == "Shift_JIS")
XCTAssertEqual(CFStringConvertEncodingToIANACharSetName(dosJapanese) as String, "cp932")
#expect(CFStringConvertEncodingToIANACharSetName(dosJapanese) as String == "cp932")
// IANA charset name -> CFStringEncoding
XCTAssertEqual(CFStringConvertIANACharSetNameToEncoding("SHIFT_JIS" as CFString), shiftJIS)
XCTAssertEqual(CFStringConvertIANACharSetNameToEncoding("shift_jis" as CFString), shiftJIS)
XCTAssertEqual(CFStringConvertIANACharSetNameToEncoding("cp932" as CFString), dosJapanese)
XCTAssertEqual(CFStringConvertIANACharSetNameToEncoding("sjis" as CFString), dosJapanese)
XCTAssertEqual(CFStringConvertIANACharSetNameToEncoding("shiftjis" as CFString), dosJapanese)
XCTAssertNotEqual(CFStringConvertIANACharSetNameToEncoding("shift_jis" as CFString), shiftJIS_X0213)
#expect(CFStringConvertIANACharSetNameToEncoding("SHIFT_JIS" as CFString) == shiftJIS)
#expect(CFStringConvertIANACharSetNameToEncoding("shift_jis" as CFString) == shiftJIS)
#expect(CFStringConvertIANACharSetNameToEncoding("cp932" as CFString) == dosJapanese)
#expect(CFStringConvertIANACharSetNameToEncoding("sjis" as CFString) == dosJapanese)
#expect(CFStringConvertIANACharSetNameToEncoding("shiftjis" as CFString) == dosJapanese)
#expect(CFStringConvertIANACharSetNameToEncoding("shift_jis" as CFString) != shiftJIS_X0213)
// `String.Encoding.shiftJIS` is "Japanese (Windows, DOS)."
XCTAssertEqual(CFStringConvertNSStringEncodingToEncoding(String.Encoding.shiftJIS.rawValue), dosJapanese)
#expect(CFStringConvertNSStringEncodingToEncoding(String.Encoding.shiftJIS.rawValue) == dosJapanese)
}
func testXattrEncoding() {
@Test func encodeXattr() {
let utf8Data = Data("utf-8;134217984".utf8)
XCTAssertEqual(String.Encoding.utf8.xattrEncodingData, utf8Data)
XCTAssertEqual(utf8Data.decodingXattrEncoding, .utf8)
XCTAssertEqual(Data("utf-8".utf8).decodingXattrEncoding, .utf8)
#expect(String.Encoding.utf8.xattrEncodingData == utf8Data)
#expect(utf8Data.decodingXattrEncoding == .utf8)
#expect(Data("utf-8".utf8).decodingXattrEncoding == .utf8)
let eucJPData = Data("euc-jp;2336".utf8)
XCTAssertEqual(String.Encoding.japaneseEUC.xattrEncodingData, eucJPData)
XCTAssertEqual(eucJPData.decodingXattrEncoding, .japaneseEUC)
XCTAssertEqual(Data("euc-jp".utf8).decodingXattrEncoding, .japaneseEUC)
#expect(String.Encoding.japaneseEUC.xattrEncodingData == eucJPData)
#expect(eucJPData.decodingXattrEncoding == .japaneseEUC)
#expect(Data("euc-jp".utf8).decodingXattrEncoding == .japaneseEUC)
}
func testYenConversion() {
@Test func convertYen() {
XCTAssertTrue("¥".canBeConverted(to: .utf8))
XCTAssertTrue("¥".canBeConverted(to: String.Encoding(cfEncodings: .shiftJIS)))
XCTAssertFalse("¥".canBeConverted(to: .shiftJIS))
XCTAssertFalse("¥".canBeConverted(to: .japaneseEUC)) // ? (U+003F)
XCTAssertFalse("¥".canBeConverted(to: .ascii)) // Y (U+0059)
#expect("¥".canBeConverted(to: .utf8))
#expect("¥".canBeConverted(to: String.Encoding(cfEncodings: .shiftJIS)))
#expect(!"¥".canBeConverted(to: .shiftJIS))
#expect(!"¥".canBeConverted(to: .japaneseEUC)) // ? (U+003F)
#expect(!"¥".canBeConverted(to: .ascii)) // Y (U+0059)
let string = "\\ ¥ yen"
XCTAssertEqual(string.convertYenSign(for: .utf8), string)
XCTAssertEqual(string.convertYenSign(for: String.Encoding(cfEncodings: .shiftJIS)), string)
XCTAssertEqual(string.convertYenSign(for: .shiftJIS), "\\ \\ yen")
XCTAssertEqual(string.convertYenSign(for: .japaneseEUC), "\\ \\ yen")
XCTAssertEqual(string.convertYenSign(for: .ascii), "\\ \\ yen")
#expect(string.convertYenSign(for: .utf8) == string)
#expect(string.convertYenSign(for: String.Encoding(cfEncodings: .shiftJIS)) == string)
#expect(string.convertYenSign(for: .shiftJIS) == "\\ \\ yen")
#expect(string.convertYenSign(for: .japaneseEUC) == "\\ \\ yen")
#expect(string.convertYenSign(for: .ascii) == "\\ \\ yen")
}
func testIANACharsetName() {
@Test func ianaCharsetName() {
XCTAssertEqual(String.Encoding.utf8.ianaCharSetName, "utf-8")
XCTAssertEqual(String.Encoding.isoLatin1.ianaCharSetName, "iso-8859-1")
#expect(String.Encoding.utf8.ianaCharSetName == "utf-8")
#expect(String.Encoding.isoLatin1.ianaCharSetName == "iso-8859-1")
}
func testYenEncoding() throws {
@Test func encodeYen() throws {
// encodings listed in faq_about_yen_backslash.html
let ascii = try XCTUnwrap(CFStringEncodings(rawValue: CFIndex(CFStringBuiltInEncodings.ASCII.rawValue)))
let ascii = try #require(CFStringEncodings(rawValue: CFIndex(CFStringBuiltInEncodings.ASCII.rawValue)))
let inHelpCFEncodings: [CFStringEncodings] = [
.dosJapanese,
.EUC_JP, // Japanese (EUC)
@ -274,10 +272,10 @@ final class EncodingDetectionTests: XCTestCase {
.filter { !"¥".canBeConverted(to: $0) }
for encoding in yenIncompatibleEncodings {
XCTAssert(inHelpEncodings.contains(encoding), "\(String.localizedName(of: encoding))")
#expect(inHelpEncodings.contains(encoding), "\(String.localizedName(of: encoding))")
}
for encoding in inHelpEncodings {
XCTAssert(availableEncodings.contains(encoding), "\(String.localizedName(of: encoding))")
#expect(availableEncodings.contains(encoding), "\(String.localizedName(of: encoding))")
}
}
}
@ -307,7 +305,7 @@ private extension EncodingDetectionTests {
func dataForFileName(_ fileName: String) throws -> Data {
guard
let fileURL = self.bundle.url(forResource: fileName, withExtension: "txt", subdirectory: "Encodings")
let fileURL = Bundle(for: type(of: self)).url(forResource: fileName, withExtension: "txt", subdirectory: "Encodings")
else { throw CocoaError(.fileNoSuchFile) }
return try Data(contentsOf: fileURL)

View File

@ -23,41 +23,53 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class FileDropItemTests: XCTestCase {
struct FileDropItemTests {
func testAvailability() {
@Test func emptyAvailability() {
let emptyItem = FileDropItem()
XCTAssertTrue(emptyItem.supports(extension: "JPG", scope: "foo"))
XCTAssertTrue(emptyItem.supports(extension: "jpg", scope: nil))
XCTAssertTrue(emptyItem.supports(extension: nil, scope: ""))
XCTAssertTrue(emptyItem.supports(extension: nil, scope: nil))
let item = FileDropItem()
#expect(item.supports(extension: "JPG", scope: "foo"))
#expect(item.supports(extension: "jpg", scope: nil))
#expect(item.supports(extension: nil, scope: ""))
#expect(item.supports(extension: nil, scope: nil))
}
let extensionItem = FileDropItem(format: "", extensions: ["jpg", "JPEG"])
XCTAssertTrue(extensionItem.supports(extension: "JPG", scope: "foo"))
XCTAssertTrue(extensionItem.supports(extension: "JPG", scope: nil))
XCTAssertFalse(extensionItem.supports(extension: "gif", scope: "foo"))
XCTAssertFalse(extensionItem.supports(extension: nil, scope: "foo"))
XCTAssertFalse(extensionItem.supports(extension: nil, scope: nil))
let scopeItem = FileDropItem(format: "", scope: "foo")
XCTAssertTrue(scopeItem.supports(extension: "JPG", scope: "foo"))
XCTAssertTrue(scopeItem.supports(extension: "gif", scope: "foo"))
XCTAssertTrue(scopeItem.supports(extension: nil, scope: "foo"))
XCTAssertFalse(scopeItem.supports(extension: nil, scope: "bar"))
XCTAssertFalse(scopeItem.supports(extension: "JPG", scope: nil))
XCTAssertFalse(scopeItem.supports(extension: nil, scope: nil))
@Test func extensionAvailability() {
let item = FileDropItem(format: "", extensions: ["jpg", "JPEG"])
#expect(item.supports(extension: "JPG", scope: "foo"))
#expect(item.supports(extension: "JPG", scope: nil))
#expect(!item.supports(extension: "gif", scope: "foo"))
#expect(!item.supports(extension: nil, scope: "foo"))
#expect(!item.supports(extension: nil, scope: nil))
}
@Test func scopeAvailability() {
let item = FileDropItem(format: "", scope: "foo")
#expect(item.supports(extension: "JPG", scope: "foo"))
#expect(item.supports(extension: "gif", scope: "foo"))
#expect(item.supports(extension: nil, scope: "foo"))
#expect(!item.supports(extension: nil, scope: "bar"))
#expect(!item.supports(extension: "JPG", scope: nil))
#expect(!item.supports(extension: nil, scope: nil))
}
@Test func mixAvailability() {
let item = FileDropItem(format: "", extensions: ["jpg", "JPEG"], scope: "foo")
XCTAssertTrue(item.supports(extension: "JPG", scope: "foo"))
XCTAssertTrue(item.supports(extension: "jpeg", scope: "foo"))
XCTAssertFalse(item.supports(extension: "gif", scope: "foo"))
XCTAssertFalse(item.supports(extension: nil, scope: "foo"))
XCTAssertFalse(item.supports(extension: nil, scope: "bar"))
XCTAssertFalse(item.supports(extension: "JPG", scope: nil))
XCTAssertFalse(item.supports(extension: nil, scope: nil))
#expect(item.supports(extension: "JPG", scope: "foo"))
#expect(item.supports(extension: "jpeg", scope: "foo"))
#expect(!item.supports(extension: "gif", scope: "foo"))
#expect(!item.supports(extension: nil, scope: "foo"))
#expect(!item.supports(extension: nil, scope: "bar"))
#expect(!item.supports(extension: "JPG", scope: nil))
#expect(!item.supports(extension: nil, scope: nil))
}
}

View File

@ -24,34 +24,34 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class FilePermissionTests: XCTestCase {
struct FilePermissionTests {
func testFilePermissions() {
@Test func filePermissions() {
XCTAssertEqual(FilePermissions(mask: 0o777).mask, 0o777)
XCTAssertEqual(FilePermissions(mask: 0o643).mask, 0o643)
#expect(FilePermissions(mask: 0o777).mask == 0o777)
#expect(FilePermissions(mask: 0o643).mask == 0o643)
XCTAssertEqual(FilePermissions(mask: 0o777).symbolic, "rwxrwxrwx")
XCTAssertEqual(FilePermissions(mask: 0o643).symbolic, "rw-r---wx")
#expect(FilePermissions(mask: 0o777).symbolic == "rwxrwxrwx")
#expect(FilePermissions(mask: 0o643).symbolic == "rw-r---wx")
}
func testFormatStyle() {
@Test func formatStyle() {
XCTAssertEqual(FilePermissions(mask: 0o777).formatted(.filePermissions(.full)), "777 (-rwxrwxrwx)")
XCTAssertEqual(FilePermissions(mask: 0o643).formatted(.filePermissions(.full)), "643 (-rw-r---wx)")
#expect(FilePermissions(mask: 0o777).formatted(.filePermissions(.full)) == "777 (-rwxrwxrwx)")
#expect(FilePermissions(mask: 0o643).formatted(.filePermissions(.full)) == "643 (-rw-r---wx)")
}
func testCalculation() {
@Test func calculate() {
var permissions = FilePermissions(mask: 0o644)
permissions.user.insert(.execute)
XCTAssertTrue(permissions.user.contains(.execute))
XCTAssertEqual(permissions.mask, 0o744)
#expect(permissions.user.contains(.execute))
#expect(permissions.mask == 0o744)
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2016-2023 1024jp
// © 2016-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,40 +24,41 @@
// limitations under the License.
//
import XCTest
import AppKit.NSFont
import Testing
@testable import CotEditor
final class FontExtensionTests: XCTestCase {
struct FontExtensionTests {
func testFontSize() {
@Test func fontSize() {
let font = NSFont(name: "Menlo-Regular", size: 11)
XCTAssertEqual(font?.width(of: " "), 6.62255859375)
#expect(font?.width(of: " ") == 6.62255859375)
}
func testFontWeight() throws {
@Test func fontWeight() throws {
let regularFont = try XCTUnwrap(NSFont(name: "Menlo-Regular", size: 11))
let boldFont = try XCTUnwrap(NSFont(name: "Menlo-Bold", size: 11))
let regularFont = try #require(NSFont(name: "Menlo-Regular", size: 11))
let boldFont = try #require(NSFont(name: "Menlo-Bold", size: 11))
XCTAssertEqual(regularFont.weight, .regular)
XCTAssertEqual(boldFont.weight.rawValue, NSFont.Weight.bold.rawValue, accuracy: 0.00001)
#expect(regularFont.weight == .regular)
// #expect(boldFont.weight.rawValue == NSFont.Weight.bold.rawValue) // accuracy: 0.00001
// The const value is (unfortunately) not exact equal...
XCTAssertEqual(boldFont.weight.rawValue, 0.4)
XCTAssertNotEqual(NSFont.Weight.bold.rawValue, 0.4)
#expect(boldFont.weight.rawValue == 0.4)
#expect(NSFont.Weight.bold.rawValue != 0.4)
}
func testNamedFont() throws {
@Test func namedFont() throws {
let menlo = try XCTUnwrap(NSFont(named: .menlo, size: 11))
XCTAssertEqual(menlo, NSFont(name: "Menlo-Regular", size: 11))
let menlo = try #require(NSFont(named: .menlo, size: 11))
#expect(menlo == NSFont(name: "Menlo-Regular", size: 11))
let avenirNextCondensed = try XCTUnwrap(NSFont(named: .avenirNextCondensed, weight: .bold, size: 11))
XCTAssertEqual(avenirNextCondensed, NSFont(name: "AvenirNextCondensed-Bold", size: 11))
XCTAssertEqual(avenirNextCondensed.weight.rawValue, NSFont.Weight.bold.rawValue, accuracy: 0.00001)
let avenirNextCondensed = try #require(NSFont(named: .avenirNextCondensed, weight: .bold, size: 11))
#expect(avenirNextCondensed == NSFont(name: "AvenirNextCondensed-Bold", size: 11))
// #expect(avenirNextCondensed.weight.rawValue == NSFont.Weight.bold.rawValue) // accuracy: 0.00001
}
}

View File

@ -23,47 +23,47 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class FormatStylesTests: XCTestCase {
struct FormatStylesTests {
func testCSVFormatStyle() {
@Test func formatCSV() throws {
XCTAssertEqual(["dog", "cat"].formatted(.csv), "dog, cat")
XCTAssertEqual(["dog"].formatted(.csv), "dog")
XCTAssertEqual(["dog", "", "dog", ""].formatted(.csv), "dog, , dog, ")
XCTAssertEqual(["dog", "", "dog", ""].formatted(.csv(omittingEmptyItems: true)), "dog, dog")
#expect(["dog", "cat"].formatted(.csv) == "dog, cat")
#expect(["dog"].formatted(.csv) == "dog")
#expect(["dog", "", "dog", ""].formatted(.csv) == "dog, , dog, ")
#expect(["dog", "", "dog", ""].formatted(.csv(omittingEmptyItems: true)) == "dog, dog")
let strategy = CSVFormatStyle().parseStrategy
XCTAssertEqual(try strategy.parse("dog, cat"), ["dog", "cat"])
XCTAssertEqual(try strategy.parse(" a,b,c"), ["a", "b", "c"])
XCTAssertEqual(try strategy.parse(" a, ,c"), ["a", "", "c"])
XCTAssertEqual(try CSVFormatStyle(omittingEmptyItems: true).parseStrategy.parse(" a,,c"), ["a", "c"])
#expect(try strategy.parse("dog, cat") == ["dog", "cat"])
#expect(try strategy.parse(" a,b,c") == ["a", "b", "c"])
#expect(try strategy.parse(" a, ,c") == ["a", "", "c"])
#expect(try CSVFormatStyle(omittingEmptyItems: true).parseStrategy.parse(" a,,c") == ["a", "c"])
}
func testRangedInteger() throws {
@Test func rangedInteger() throws {
let formatter = RangedIntegerFormatStyle(range: 1...(.max))
XCTAssertEqual(formatter.format(-3), "1")
XCTAssertEqual(try formatter.parseStrategy.parse("0"), 1)
XCTAssertEqual(try formatter.parseStrategy.parse("1"), 1)
XCTAssertEqual(try formatter.parseStrategy.parse("2"), 2)
XCTAssertEqual(try formatter.parseStrategy.parse("a"), 1)
#expect(formatter.format(-3) == "1")
#expect(try formatter.parseStrategy.parse("0") == 1)
#expect(try formatter.parseStrategy.parse("1") == 1)
#expect(try formatter.parseStrategy.parse("2") == 2)
#expect(try formatter.parseStrategy.parse("a") == 1)
}
func testRangedIntegerWithDefault() throws {
@Test func rangedIntegerWithDefault() throws {
let formatter = RangedIntegerFormatStyle(range: -1...(.max), defaultValue: 4)
XCTAssertEqual(formatter.format(-3), "-1")
XCTAssertEqual(try formatter.parseStrategy.parse("-2"), -1)
XCTAssertEqual(try formatter.parseStrategy.parse("-1"), -1)
XCTAssertEqual(try formatter.parseStrategy.parse("0"), 0)
XCTAssertEqual(try formatter.parseStrategy.parse("2"), 2)
XCTAssertEqual(try formatter.parseStrategy.parse("a"), 4)
#expect(formatter.format(-3) == "-1")
#expect(try formatter.parseStrategy.parse("-2") == -1)
#expect(try formatter.parseStrategy.parse("-1") == -1)
#expect(try formatter.parseStrategy.parse("0") == 0)
#expect(try formatter.parseStrategy.parse("2") == 2)
#expect(try formatter.parseStrategy.parse("a") == 4)
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2016-2020 1024jp
// © 2016-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,14 +24,15 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class FourCharCodeTests: XCTestCase {
struct FourCharCodeTests {
func testInitializer() {
@Test func initialize() {
XCTAssertEqual(FourCharCode(stringLiteral: "TEXT"), NSHFSTypeCodeFromFileType("'TEXT'"))
XCTAssertEqual("rtfd", NSHFSTypeCodeFromFileType("'rtfd'"))
#expect(FourCharCode(stringLiteral: "TEXT") == NSHFSTypeCodeFromFileType("'TEXT'"))
#expect("rtfd" == NSHFSTypeCodeFromFileType("'rtfd'"))
}
}

View File

@ -23,114 +23,116 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class FuzzyRangeTests: XCTestCase {
struct FuzzyRangeTests {
func testFuzzyCharacterRange() {
@Test func fuzzyCharacterRange() {
let string = "0123456789"
XCTAssertEqual(string.range(in: FuzzyRange(location: 2, length: 2)), NSRange(location: 2, length: 2))
XCTAssertEqual(string.range(in: FuzzyRange(location: -1, length: 0)), NSRange(location: 10, length: 0))
XCTAssertEqual(string.range(in: FuzzyRange(location: -2, length: 1)), NSRange(location: 9, length: 1))
XCTAssertEqual(string.range(in: FuzzyRange(location: 3, length: -1)), NSRange(3..<9))
XCTAssertEqual(string.range(in: FuzzyRange(location: 3, length: -2)), NSRange(location: 3, length: "45678".utf16.count))
#expect(string.range(in: FuzzyRange(location: 2, length: 2)) == NSRange(location: 2, length: 2))
#expect(string.range(in: FuzzyRange(location: -1, length: 0)) == NSRange(location: 10, length: 0))
#expect(string.range(in: FuzzyRange(location: -2, length: 1)) == NSRange(location: 9, length: 1))
#expect(string.range(in: FuzzyRange(location: 3, length: -1)) == NSRange(3..<9))
#expect(string.range(in: FuzzyRange(location: 3, length: -2)) == NSRange(location: 3, length: "45678".utf16.count))
// grapheme cluster count
XCTAssertEqual("black 🐈‍⬛ cat".range(in: FuzzyRange(location: 6, length: 2)), NSRange(location: 6, length: 5))
#expect("black 🐈‍⬛ cat".range(in: FuzzyRange(location: 6, length: 2)) == NSRange(location: 6, length: 5))
}
func testFuzzyLineRange() throws {
@Test func fuzzyLineRange() throws {
let string = "1\r\n2\r\n3\r\n4" // 1 based
var range: NSRange
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 1, length: 2)))
XCTAssertEqual((string as NSString).substring(with: range), "1\r\n2\r\n")
range = try #require(string.rangeForLine(in: FuzzyRange(location: 1, length: 2)))
#expect((string as NSString).substring(with: range) == "1\r\n2\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 4, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "4")
range = try #require(string.rangeForLine(in: FuzzyRange(location: 4, length: 1)))
#expect((string as NSString).substring(with: range) == "4")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 3, length: 0)))
XCTAssertEqual((string as NSString).substring(with: range), "3\r\n")
range = try #require(string.rangeForLine(in: FuzzyRange(location: 3, length: 0)))
#expect((string as NSString).substring(with: range) == "3\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: -1, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "4")
range = try #require(string.rangeForLine(in: FuzzyRange(location: -1, length: 1)))
#expect((string as NSString).substring(with: range) == "4")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: -2, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "3\r\n")
range = try #require(string.rangeForLine(in: FuzzyRange(location: -2, length: 1)))
#expect((string as NSString).substring(with: range) == "3\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 2, length: -2)))
XCTAssertEqual((string as NSString).substring(with: range), "2\r\n")
range = try #require(string.rangeForLine(in: FuzzyRange(location: 2, length: -2)))
#expect((string as NSString).substring(with: range) == "2\r\n")
range = try XCTUnwrap("1\n".rangeForLine(in: FuzzyRange(location: -1, length: 0)))
XCTAssertEqual(range, NSRange(location: 2, length: 0))
range = try #require("1\n".rangeForLine(in: FuzzyRange(location: -1, length: 0)))
#expect(range == NSRange(location: 2, length: 0))
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 1, length: 2), includingLineEnding: false))
XCTAssertEqual((string as NSString).substring(with: range), "1\r\n2")
range = try #require(string.rangeForLine(in: FuzzyRange(location: 1, length: 2), includingLineEnding: false))
#expect((string as NSString).substring(with: range) == "1\r\n2")
}
func testFormattingFuzzyRange() {
@Test func formatFuzzyRange() {
XCTAssertEqual(FuzzyRange(location: 0, length: 0).formatted(), "0")
XCTAssertEqual(FuzzyRange(location: 1, length: 0).formatted(), "1")
XCTAssertEqual(FuzzyRange(location: 1, length: 1).formatted(), "1")
XCTAssertEqual(FuzzyRange(location: 1, length: 2).formatted(), "1:2")
XCTAssertEqual(FuzzyRange(location: -1, length: 0).formatted(), "-1")
XCTAssertEqual(FuzzyRange(location: -1, length: -1).formatted(), "-1:-1")
#expect(FuzzyRange(location: 0, length: 0).formatted() == "0")
#expect(FuzzyRange(location: 1, length: 0).formatted() == "1")
#expect(FuzzyRange(location: 1, length: 1).formatted() == "1")
#expect(FuzzyRange(location: 1, length: 2).formatted() == "1:2")
#expect(FuzzyRange(location: -1, length: 0).formatted() == "-1")
#expect(FuzzyRange(location: -1, length: -1).formatted() == "-1:-1")
}
func testParsingFuzzyRange() throws {
@Test func parseFuzzyRange() throws {
let parser = FuzzyRangeParseStrategy()
XCTAssertEqual(try parser.parse("0"), FuzzyRange(location: 0, length: 0))
XCTAssertEqual(try parser.parse("1"), FuzzyRange(location: 1, length: 0))
XCTAssertEqual(try parser.parse("1:2"), FuzzyRange(location: 1, length: 2))
XCTAssertEqual(try parser.parse("-1"), FuzzyRange(location: -1, length: 0))
XCTAssertEqual(try parser.parse("-1:-1"), FuzzyRange(location: -1, length: -1))
XCTAssertThrowsError(try parser.parse(""))
XCTAssertThrowsError(try parser.parse("abc"))
XCTAssertThrowsError(try parser.parse("1:a"))
XCTAssertThrowsError(try parser.parse("1:1:1"))
#expect(try parser.parse("0") == FuzzyRange(location: 0, length: 0))
#expect(try parser.parse("1") == FuzzyRange(location: 1, length: 0))
#expect(try parser.parse("1:2") == FuzzyRange(location: 1, length: 2))
#expect(try parser.parse("-1") == FuzzyRange(location: -1, length: 0))
#expect(try parser.parse("-1:-1") == FuzzyRange(location: -1, length: -1))
#expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("") }
#expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("abc") }
#expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("1:a") }
#expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("1:1:1") }
}
func testFuzzyLocation() throws {
@Test func fuzzyLocation() throws {
let string = "1\r\n2\r\n3\r\n456\n567" // 1 based
XCTAssertEqual(try string.fuzzyLocation(line: 0), 0)
XCTAssertEqual(try string.fuzzyLocation(line: 0, column: 1), 1)
#expect(try string.fuzzyLocation(line: 0) == 0)
#expect(try string.fuzzyLocation(line: 0, column: 1) == 1)
XCTAssertEqual(try string.fuzzyLocation(line: 1), 0)
XCTAssertEqual(try string.fuzzyLocation(line: 2), 3)
XCTAssertEqual(try string.fuzzyLocation(line: 4), 9)
XCTAssertEqual(try string.fuzzyLocation(line: 5), 13)
XCTAssertEqual(try string.fuzzyLocation(line: -1), 13)
XCTAssertEqual(try string.fuzzyLocation(line: -2), 9)
XCTAssertEqual(try string.fuzzyLocation(line: -5), 0)
XCTAssertThrowsError(try string.fuzzyLocation(line: -6))
#expect(try string.fuzzyLocation(line: 1) == 0)
#expect(try string.fuzzyLocation(line: 2) == 3)
#expect(try string.fuzzyLocation(line: 4) == 9)
#expect(try string.fuzzyLocation(line: 5) == 13)
#expect(try string.fuzzyLocation(line: -1) == 13)
#expect(try string.fuzzyLocation(line: -2) == 9)
#expect(try string.fuzzyLocation(line: -5) == 0)
#expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: -6) }
// line with a line ending
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 0), 9)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 1), 10)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 3), 12)
XCTAssertThrowsError(try string.fuzzyLocation(line: 4, column: 4))
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: -1), 12)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: -2), 11)
#expect(try string.fuzzyLocation(line: 4, column: 0) == 9)
#expect(try string.fuzzyLocation(line: 4, column: 1) == 10)
#expect(try string.fuzzyLocation(line: 4, column: 3) == 12)
#expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: 4, column: 4) }
#expect(try string.fuzzyLocation(line: 4, column: -1) == 12)
#expect(try string.fuzzyLocation(line: 4, column: -2) == 11)
// line without any line endings (the last line)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 0), 13)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 1), 14)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 3), 16)
XCTAssertThrowsError(try string.fuzzyLocation(line: 5, column: 4))
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: -1), 16)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: -2), 15)
#expect(try string.fuzzyLocation(line: 5, column: 0) == 13)
#expect(try string.fuzzyLocation(line: 5, column: 1) == 14)
#expect(try string.fuzzyLocation(line: 5, column: 3) == 16)
#expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: 5, column: 4) }
#expect(try string.fuzzyLocation(line: 5, column: -1) == 16)
#expect(try string.fuzzyLocation(line: 5, column: -2) == 15)
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2016-2022 1024jp
// © 2016-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,37 +24,38 @@
// limitations under the License.
//
import XCTest
import CoreGraphics
import Testing
@testable import CotEditor
final class GeometryTests: XCTestCase {
struct GeometryTests {
func testScaling() {
@Test func scale() {
XCTAssertEqual(CGSize.unit.scaled(to: 0.5), CGSize(width: 0.5, height: 0.5))
XCTAssertEqual(CGPoint(x: 2, y: 1).scaled(to: 2), CGPoint(x: 4, y: 2))
XCTAssertEqual(CGRect(x: 2, y: 1, width: 2, height: 1).scaled(to: 2),
#expect(CGSize.unit.scaled(to: 0.5) == CGSize(width: 0.5, height: 0.5))
#expect(CGPoint(x: 2, y: 1).scaled(to: 2) == CGPoint(x: 4, y: 2))
#expect(CGRect(x: 2, y: 1, width: 2, height: 1).scaled(to: 2) ==
CGRect(x: 4, y: 2, width: 4, height: 2))
}
func testRectMid() {
@Test func rectMid() {
XCTAssertEqual(CGRect(x: 1, y: 2, width: 2, height: 4).mid, CGPoint(x: 2, y: 4))
#expect(CGRect(x: 1, y: 2, width: 2, height: 4).mid == CGPoint(x: 2, y: 4))
}
func testPrefix() {
@Test func prefix() {
XCTAssertEqual(-CGPoint(x: 2, y: 3), CGPoint(x: -2, y: -3))
XCTAssertEqual(-CGSize(width: 2, height: 3), CGSize(width: -2, height: -3))
#expect(-CGPoint(x: 2, y: 3) == CGPoint(x: -2, y: -3))
#expect(-CGSize(width: 2, height: 3) == CGSize(width: -2, height: -3))
}
func testOffset() {
@Test func offset() {
XCTAssertEqual(CGPoint(x: 2, y: 3).offsetBy(dx: 4, dy: 5), CGPoint(x: 6, y: 8))
XCTAssertEqual(CGPoint(x: 2, y: 3).offset(by: -CGPoint(x: 2, y: 3)), .zero)
XCTAssertEqual(CGPoint(x: 2, y: 3).offset(by: -CGSize(width: 2, height: 3)), .zero)
#expect(CGPoint(x: 2, y: 3).offsetBy(dx: 4, dy: 5) == CGPoint(x: 6, y: 8))
#expect(CGPoint(x: 2, y: 3).offset(by: -CGPoint(x: 2, y: 3)) == .zero)
#expect(CGPoint(x: 2, y: 3).offset(by: -CGSize(width: 2, height: 3)) == .zero)
}
}

View File

@ -24,61 +24,62 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class IncompatibleCharacterTests: XCTestCase {
struct IncompatibleCharacterTests {
func testIncompatibleCharacterScan() throws {
@Test func scanIncompatibleCharacter() throws {
let string = "abc\\ \n ¥ \n ~"
let incompatibles = try string.charactersIncompatible(with: .plainShiftJIS)
XCTAssertEqual(incompatibles.count, 2)
#expect(incompatibles.count == 2)
let backslash = try XCTUnwrap(incompatibles.first)
let backslash = try #require(incompatibles.first)
XCTAssertEqual(backslash.value.character, "\\")
XCTAssertEqual(backslash.value.converted, "")
XCTAssertEqual(backslash.location, 3)
#expect(backslash.value.character == "\\")
#expect(backslash.value.converted == "")
#expect(backslash.location == 3)
let tilde = incompatibles[1]
XCTAssertEqual(tilde.value.character, "~")
XCTAssertEqual(tilde.value.converted, "?")
XCTAssertEqual(tilde.location, 11)
#expect(tilde.value.character == "~")
#expect(tilde.value.converted == "?")
#expect(tilde.location == 11)
}
func testSequentialIncompatibleCharactersScan() throws {
@Test func scanSequentialIncompatibleCharacters() throws {
let string = "~~"
let incompatibles = try string.charactersIncompatible(with: .plainShiftJIS)
XCTAssertEqual(incompatibles.count, 2)
#expect(incompatibles.count == 2)
let tilde = incompatibles[1]
XCTAssertEqual(tilde.value.character, "~")
XCTAssertEqual(tilde.value.converted, "?")
XCTAssertEqual(tilde.location, 1)
#expect(tilde.value.character == "~")
#expect(tilde.value.converted == "?")
#expect(tilde.location == 1)
}
func testIncompatibleCharacterScanWithLengthShift() throws {
@Test func scanIncompatibleCharacterWithLengthShift() throws {
let string = "family 👨‍👨‍👦 with 🐕"
let incompatibles = try string.charactersIncompatible(with: .japaneseEUC)
XCTAssertEqual(incompatibles.count, 2)
#expect(incompatibles.count == 2)
XCTAssertEqual(incompatibles[0].value.character, "👨‍👨‍👦")
XCTAssertEqual(incompatibles[0].value.converted, "????????")
XCTAssertEqual(incompatibles[0].location, 7)
#expect(incompatibles[0].value.character == "👨‍👨‍👦")
#expect(incompatibles[0].value.converted == "????????")
#expect(incompatibles[0].location == 7)
XCTAssertEqual(incompatibles[1].value.character, "🐕")
XCTAssertEqual(incompatibles[1].value.converted, "??")
XCTAssertEqual(incompatibles[1].location, 21)
#expect(incompatibles[1].value.character == "🐕")
#expect(incompatibles[1].value.converted == "??")
#expect(incompatibles[1].location == 21)
}
}

View File

@ -23,12 +23,13 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class LineEndingScannerTests: XCTestCase {
struct LineEndingScannerTests {
func testScanner() {
@Test func scan() {
let storage = NSTextStorage(string: "dog\ncat\r\ncow")
let scanner = LineEndingScanner(textStorage: storage, lineEnding: .lf)
@ -36,28 +37,28 @@ final class LineEndingScannerTests: XCTestCase {
storage.replaceCharacters(in: NSRange(0..<3), with: "dog\u{85}cow")
// test line ending scan
XCTAssertEqual(scanner.inconsistentLineEndings,
#expect(scanner.inconsistentLineEndings ==
[ValueRange(value: .nel, range: NSRange(location: 3, length: 1)),
ValueRange(value: .crlf, range: NSRange(location: 11, length: 2))])
}
func testEmpty() {
@Test func empty() {
let storage = NSTextStorage(string: "\r")
let scanner = LineEndingScanner(textStorage: storage, lineEnding: .lf)
XCTAssertEqual(scanner.inconsistentLineEndings, [ValueRange(value: .cr, range: NSRange(location: 0, length: 1))])
#expect(scanner.inconsistentLineEndings == [ValueRange(value: .cr, range: NSRange(location: 0, length: 1))])
// test scanRange does not expand to the out of range
storage.replaceCharacters(in: NSRange(0..<1), with: "")
// test line ending scan
XCTAssert(scanner.inconsistentLineEndings.isEmpty)
#expect(scanner.inconsistentLineEndings.isEmpty)
}
func testCRLFEditing() {
@Test func editCRLF() {
let storage = NSTextStorage(string: "dog\ncat\r\ncow")
let scanner = LineEndingScanner(textStorage: storage, lineEnding: .lf)
@ -68,63 +69,62 @@ final class LineEndingScannerTests: XCTestCase {
storage.replaceCharacters(in: NSRange(9..<10), with: "")
// test line ending scan
XCTAssertEqual(scanner.inconsistentLineEndings,
#expect(scanner.inconsistentLineEndings ==
[ValueRange(value: .crlf, range: NSRange(location: 3, length: 2)),
ValueRange(value: .cr, range: NSRange(location: 8, length: 1))])
}
func testDetection() {
@Test func detect() {
let storage = NSTextStorage()
let scanner = LineEndingScanner(textStorage: storage, lineEnding: .lf)
XCTAssertNil(scanner.majorLineEnding)
#expect(scanner.majorLineEnding == nil)
storage.string = "a"
XCTAssertNil(scanner.majorLineEnding)
#expect(scanner.majorLineEnding == nil)
storage.string = "\n"
XCTAssertEqual(scanner.majorLineEnding, .lf)
#expect(scanner.majorLineEnding == .lf)
storage.string = "\r"
XCTAssertEqual(scanner.majorLineEnding, .cr)
#expect(scanner.majorLineEnding == .cr)
storage.string = "\r\n"
XCTAssertEqual(scanner.majorLineEnding, .crlf)
#expect(scanner.majorLineEnding == .crlf)
storage.string = "\u{85}"
XCTAssertEqual(scanner.majorLineEnding, .nel)
#expect(scanner.majorLineEnding == .nel)
storage.string = "abc\u{2029}def"
XCTAssertEqual(scanner.majorLineEnding, .paragraphSeparator)
#expect(scanner.majorLineEnding == .paragraphSeparator)
storage.string = "\rfoo\r\nbar\nbuz\u{2029}moin\r\n"
XCTAssertEqual(scanner.majorLineEnding, .crlf) // most used new line must be detected
#expect(scanner.majorLineEnding == .crlf) // most used new line must be detected
}
func testLineNumberCalculation() {
@Test func calculateLineNumber() {
let storage = NSTextStorage(string: "dog \n\n cat \n cow \n")
let scanner = LineEndingScanner(textStorage: storage, lineEnding: .lf)
XCTAssertEqual(scanner.lineNumber(at: 0), 1)
XCTAssertEqual(scanner.lineNumber(at: 1), 1)
XCTAssertEqual(scanner.lineNumber(at: 4), 1)
XCTAssertEqual(scanner.lineNumber(at: 5), 2)
XCTAssertEqual(scanner.lineNumber(at: 6), 3)
XCTAssertEqual(scanner.lineNumber(at: 11), 3)
XCTAssertEqual(scanner.lineNumber(at: 12), 4)
XCTAssertEqual(scanner.lineNumber(at: 17), 4)
XCTAssertEqual(scanner.lineNumber(at: 18), 5)
#expect(scanner.lineNumber(at: 0) == 1)
#expect(scanner.lineNumber(at: 1) == 1)
#expect(scanner.lineNumber(at: 4) == 1)
#expect(scanner.lineNumber(at: 5) == 2)
#expect(scanner.lineNumber(at: 6) == 3)
#expect(scanner.lineNumber(at: 11) == 3)
#expect(scanner.lineNumber(at: 12) == 4)
#expect(scanner.lineNumber(at: 17) == 4)
#expect(scanner.lineNumber(at: 18) == 5)
for _ in 0..<20 {
storage.string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
for index in (0..<storage.length).shuffled() {
XCTAssertEqual(scanner.lineNumber(at: index),
storage.string.lineNumber(at: index),
#expect(scanner.lineNumber(at: index) == storage.string.lineNumber(at: index),
"At \(index) with string \"\(storage.string)\"")
}
}

View File

@ -24,28 +24,29 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class LineEndingTests: XCTestCase {
struct LineEndingTests {
func testLineEnding() {
@Test func lineEnding() {
XCTAssertEqual(LineEnding.lf.rawValue, "\n")
XCTAssertEqual(LineEnding.crlf.rawValue, "\r\n")
XCTAssertEqual(LineEnding.paragraphSeparator.rawValue, "\u{2029}")
#expect(LineEnding.lf.rawValue == "\n")
#expect(LineEnding.crlf.rawValue == "\r\n")
#expect(LineEnding.paragraphSeparator.rawValue == "\u{2029}")
}
func testName() {
@Test func name() {
XCTAssertEqual(LineEnding.lf.label, "LF")
XCTAssertEqual(LineEnding.crlf.label, "CRLF")
XCTAssertEqual(LineEnding.paragraphSeparator.label, "PS")
#expect(LineEnding.lf.label == "LF")
#expect(LineEnding.crlf.label == "CRLF")
#expect(LineEnding.paragraphSeparator.label == "PS")
}
func testLineEndingRanges() {
@Test func lineEndingRanges() {
let string = "\rfoo\r\nbar \n \nb \n\r uz\u{2029}moin\r\n"
let expected: [ValueRange<LineEnding>] = [
@ -59,16 +60,16 @@ final class LineEndingTests: XCTestCase {
.init(value: .crlf, location: 25),
]
XCTAssert("".lineEndingRanges().isEmpty)
XCTAssert("abc".lineEndingRanges().isEmpty)
XCTAssertEqual(string.lineEndingRanges(), expected)
#expect("".lineEndingRanges().isEmpty)
#expect("abc".lineEndingRanges().isEmpty)
#expect(string.lineEndingRanges() == expected)
}
func testReplacement() {
@Test func replace() {
XCTAssertEqual("foo\r\nbar\n".replacingLineEndings(with: .cr), "foo\rbar\r")
XCTAssertEqual("foo\u{c}bar\n".replacingLineEndings(with: .cr), "foo\u{c}bar\r")
#expect("foo\r\nbar\n".replacingLineEndings(with: .cr) == "foo\rbar\r")
#expect("foo\u{c}bar\n".replacingLineEndings(with: .cr) == "foo\u{c}bar\r")
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2020-2023 1024jp
// © 2020-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,29 +23,30 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class LineRangeCacheableTests: XCTestCase {
struct LineRangeCacheableTests {
private let repeatCount = 20
func testLineNumberCalculation() {
@Test func calculateLineNumber() {
let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineNumber(at: 0), 1)
XCTAssertEqual(lineString.lineNumber(at: 1), 1)
XCTAssertEqual(lineString.lineNumber(at: 4), 1)
XCTAssertEqual(lineString.lineNumber(at: 5), 2)
XCTAssertEqual(lineString.lineNumber(at: 6), 3)
XCTAssertEqual(lineString.lineNumber(at: 11), 3)
XCTAssertEqual(lineString.lineNumber(at: 12), 4)
XCTAssertEqual(lineString.lineNumber(at: 17), 4)
XCTAssertEqual(lineString.lineNumber(at: 18), 5)
#expect(lineString.lineNumber(at: 0) == 1)
#expect(lineString.lineNumber(at: 1) == 1)
#expect(lineString.lineNumber(at: 4) == 1)
#expect(lineString.lineNumber(at: 5) == 2)
#expect(lineString.lineNumber(at: 6) == 3)
#expect(lineString.lineNumber(at: 11) == 3)
#expect(lineString.lineNumber(at: 12) == 4)
#expect(lineString.lineNumber(at: 17) == 4)
#expect(lineString.lineNumber(at: 18) == 5)
let lineString2 = LineString("dog \n\n cat \n cow ")
XCTAssertEqual(lineString2.lineNumber(at: 17), 4)
#expect(lineString2.lineNumber(at: 17) == 4)
for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
@ -53,27 +54,28 @@ final class LineRangeCacheableTests: XCTestCase {
for index in (0...string.length).shuffled() {
let result = (string as NSString).lineNumber(at: index)
XCTAssertEqual(lineString.lineNumber(at: index), result, "At \(index) with string \"\(string)\"")
#expect(lineString.lineNumber(at: index) == result,
"At \(index) with string \"\(string)\"")
}
}
}
func testIndexToLineRangeCalculation() {
@Test func calculateIndexToLineRange() {
let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineRange(at: 0), NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 5), NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(at: 6), NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(at: 11), NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(at: 12), NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(at: 17), NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(at: 18), NSRange(18..<18))
#expect(lineString.lineRange(at: 0) == NSRange(0..<5))
#expect(lineString.lineRange(at: 1) == NSRange(0..<5))
#expect(lineString.lineRange(at: 4) == NSRange(0..<5))
#expect(lineString.lineRange(at: 5) == NSRange(5..<6))
#expect(lineString.lineRange(at: 6) == NSRange(6..<12))
#expect(lineString.lineRange(at: 11) == NSRange(6..<12))
#expect(lineString.lineRange(at: 12) == NSRange(12..<18))
#expect(lineString.lineRange(at: 17) == NSRange(12..<18))
#expect(lineString.lineRange(at: 18) == NSRange(18..<18))
let lineString2 = LineString("dog \n\n cat \n cow ")
XCTAssertEqual(lineString2.lineRange(at: 17), NSRange(12..<17))
#expect(lineString2.lineRange(at: 17) == NSRange(12..<17))
for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
@ -81,66 +83,69 @@ final class LineRangeCacheableTests: XCTestCase {
for index in (0...string.length).shuffled() {
let result = (string as NSString).lineRange(at: index)
XCTAssertEqual(lineString.lineRange(at: index), result, "At \(index) with string \"\(string)\"")
XCTAssertEqual(lineString.lineStartIndex(at: index), result.lowerBound, "At \(index) with string \"\(string)\"")
#expect(lineString.lineRange(at: index) == result,
"At \(index) with string \"\(string)\"")
#expect(lineString.lineStartIndex(at: index) == result.lowerBound,
"At \(index) with string \"\(string)\"")
}
}
}
func testLineContentRange() {
@Test func lineContentRange() {
let lineString = LineString("dog \n\n cat \n cow")
XCTAssertEqual(lineString.lineContentRange(for: NSRange(0..<3)), NSRange(0..<4))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(4..<6)), NSRange(0..<6))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(5..<6)), NSRange(5..<6))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(7..<13)), NSRange(6..<16))
#expect(lineString.lineContentRange(for: NSRange(0..<3)) == NSRange(0..<4))
#expect(lineString.lineContentRange(for: NSRange(4..<6)) == NSRange(0..<6))
#expect(lineString.lineContentRange(for: NSRange(5..<6)) == NSRange(5..<6))
#expect(lineString.lineContentRange(for: NSRange(7..<13)) == NSRange(6..<16))
}
func testRangeToLineRangeCalculation() throws {
@Test func calculateRangeToLineRange() throws {
let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<3)), NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<5)), NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<6)), NSRange(0..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<5)), NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<6)), NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<7)), NSRange(5..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<6)), NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<7)), NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<17)), NSRange(6..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(17..<17)), NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(17..<18)), NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(18..<18)), NSRange(18..<18))
#expect(lineString.lineRange(for: NSRange(0..<3)) == NSRange(0..<5))
#expect(lineString.lineRange(for: NSRange(0..<5)) == NSRange(0..<5))
#expect(lineString.lineRange(for: NSRange(0..<6)) == NSRange(0..<6))
#expect(lineString.lineRange(for: NSRange(5..<5)) == NSRange(5..<6))
#expect(lineString.lineRange(for: NSRange(5..<6)) == NSRange(5..<6))
#expect(lineString.lineRange(for: NSRange(5..<7)) == NSRange(5..<12))
#expect(lineString.lineRange(for: NSRange(6..<6)) == NSRange(6..<12))
#expect(lineString.lineRange(for: NSRange(6..<7)) == NSRange(6..<12))
#expect(lineString.lineRange(for: NSRange(6..<17)) == NSRange(6..<18))
#expect(lineString.lineRange(for: NSRange(17..<17)) == NSRange(12..<18))
#expect(lineString.lineRange(for: NSRange(17..<18)) == NSRange(12..<18))
#expect(lineString.lineRange(for: NSRange(18..<18)) == NSRange(18..<18))
let lineString2 = LineString("dog \n\n cat \n cow ")
XCTAssertEqual(lineString2.lineRange(for: NSRange(15..<17)), NSRange(12..<17))
XCTAssertEqual(lineString2.lineRange(for: NSRange(17..<17)), NSRange(12..<17))
#expect(lineString2.lineRange(for: NSRange(15..<17)) == NSRange(12..<17))
#expect(lineString2.lineRange(for: NSRange(17..<17)) == NSRange(12..<17))
for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
let lineString = LineString(string)
for index in (0...string.length).shuffled() {
let endIndex = try XCTUnwrap((index...string.length).randomElement())
let endIndex = try #require((index...string.length).randomElement())
let range = NSRange(index..<endIndex)
let result = (string as NSString).lineRange(for: range)
XCTAssertEqual(lineString.lineRange(for: range), result, "At \(index) with string \"\(string)\"")
#expect(lineString.lineRange(for: range) == result,
"At \(index) with string \"\(string)\"")
}
}
}
func testStringInvalidation() {
@Test func invalidateString() {
let lineString = LineString("\n🐶")
let lineNumber = lineString.lineNumber(at: 1)
let lineRange = lineString.lineRange(at: 1)
lineString.invalidateLineRanges(in: NSRange(1..<2), changeInLength: 0)
XCTAssertEqual(lineString.lineNumber(at: 1), lineNumber) // 2
XCTAssertEqual(lineString.lineRange(at: 1), lineRange) // NSRange(1..<3)
#expect(lineString.lineNumber(at: 1) == lineNumber) // 2
#expect(lineString.lineRange(at: 1) == lineRange) // NSRange(1..<3)
for _ in 0..<self.repeatCount {
let lineString = LineString(String(" 🐶 \n 🐱 \n 🐮 \n".shuffled()))
@ -152,58 +157,61 @@ final class LineRangeCacheableTests: XCTestCase {
lineString.invalidateLineRanges(in: range, changeInLength: 0)
XCTAssertEqual(lineString.lineNumber(at: index), lineNumber, "At \(index) with string \"\(lineString.string)\"")
XCTAssertEqual(lineString.lineRange(at: index), lineRange, "At \(index) with string \"\(lineString.string)\"")
XCTAssertEqual(lineString.lineStartIndex(at: index), lineRange.lowerBound, "At \(index) with string \"\(lineString.string)\"")
#expect(lineString.lineNumber(at: index) == lineNumber,
"At \(index) with string \"\(lineString.string)\"")
#expect(lineString.lineRange(at: index) == lineRange,
"At \(index) with string \"\(lineString.string)\"")
#expect(lineString.lineStartIndex(at: index) == lineRange.lowerBound,
"At \(index) with string \"\(lineString.string)\"")
}
}
}
func testStringRemoval() {
@Test func removeString() {
let lineString = LineString("dog \n\n\n cat \n ")
_ = lineString.lineNumber(at: lineString.string.length)
lineString.replaceCharacters(in: NSRange(1..<3), with: "") // "og"
XCTAssertEqual(lineString.string, "d \n\n\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<3)) // "d \n"
XCTAssertEqual(lineString.lineNumber(at: 3), 2)
XCTAssertEqual(lineString.lineRange(at: 3), NSRange(3..<4)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(4..<5)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 5), NSRange(5..<11)) // " cat \n"
#expect(lineString.string == "d \n\n\n cat \n ")
#expect(lineString.lineNumber(at: 1) == 1)
#expect(lineString.lineRange(at: 1) == NSRange(0..<3)) // "d \n"
#expect(lineString.lineNumber(at: 3) == 2)
#expect(lineString.lineRange(at: 3) == NSRange(3..<4)) // "\n"
#expect(lineString.lineRange(at: 4) == NSRange(4..<5)) // "\n"
#expect(lineString.lineRange(at: 5) == NSRange(5..<11)) // " cat \n"
lineString.replaceCharacters(in: NSRange(1..<2), with: "") // 1st " "
XCTAssertEqual(lineString.string, "d\n\n\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<2)) // "d\n"
XCTAssertEqual(lineString.lineRange(at: 2), NSRange(2..<3)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 3), NSRange(3..<4)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(4..<10)) // " cat \n"
#expect(lineString.string == "d\n\n\n cat \n ")
#expect(lineString.lineNumber(at: 1) == 1)
#expect(lineString.lineRange(at: 1) == NSRange(0..<2)) // "d\n"
#expect(lineString.lineRange(at: 2) == NSRange(2..<3)) // "\n"
#expect(lineString.lineRange(at: 3) == NSRange(3..<4)) // "\n"
#expect(lineString.lineRange(at: 4) == NSRange(4..<10)) // " cat \n"
lineString.replaceCharacters(in: NSRange(2..<4), with: "") // "\n\n"
XCTAssertEqual(lineString.string, "d\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<2)) // "d\n"
XCTAssertEqual(lineString.lineRange(at: 2), NSRange(2..<8)) // " cat \n"
#expect(lineString.string == "d\n cat \n ")
#expect(lineString.lineNumber(at: 1) == 1)
#expect(lineString.lineRange(at: 1) == NSRange(0..<2)) // "d\n"
#expect(lineString.lineRange(at: 2) == NSRange(2..<8)) // " cat \n"
}
func testStringModification() {
@Test func modifyString() {
let lineString = LineString("\n🐶")
_ = lineString.lineNumber(at: 1)
lineString.replaceCharacters(in: NSRange(1..<3), with: "a\nb")
lineString.invalidateLineRanges(in: NSRange(1..<3), changeInLength: 1)
XCTAssertEqual(lineString.lineNumber(at: 1), 2)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(1..<3)) // "a\n"
#expect(lineString.lineNumber(at: 1) == 2)
#expect(lineString.lineRange(at: 1) == NSRange(1..<3)) // "a\n"
for _ in 0..<self.repeatCount {
let string = String(" dog \n cat \n cow \n".shuffled())
let lineString = LineString(string)
XCTAssertEqual(lineString.lineNumber(at: string.length), 4)
#expect(lineString.lineNumber(at: string.length) == 4)
let location = Int.random(in: 0..<(string.length - 1))
let length = Int.random(in: 0..<(string.length - location))
@ -213,30 +221,29 @@ final class LineRangeCacheableTests: XCTestCase {
lineString.replaceCharacters(in: range, with: replacement)
for index in (0...lineString.string.length).shuffled() {
XCTAssertEqual(lineString.lineNumber(at: index), lineString.string.lineNumber(at: index),
#expect(lineString.lineNumber(at: index) == lineString.string.lineNumber(at: index),
"at \(index) with string \"\(lineString.string)\"")
XCTAssertEqual(lineString.lineRange(at: index), lineString.string.lineRange(at: index),
#expect(lineString.lineRange(at: index) == lineString.string.lineRange(at: index),
"at \(index) with string \"\(lineString.string)\"")
XCTAssertEqual(lineString.lineStartIndex(at: index), lineString.string.lineStartIndex(at: index),
#expect(lineString.lineStartIndex(at: index) == lineString.string.lineStartIndex(at: index),
"at \(index) with string \"\(lineString.string)\"")
}
}
}
func testEdgeModification() {
@Test func modifyEdge() {
let lineString = LineString("\n \n")
XCTAssertEqual(lineString.lineNumber(at: 4), 3)
#expect(lineString.lineNumber(at: 4) == 3)
lineString.replaceCharacters(in: NSRange(0..<0), with: " ")
let index = 4
XCTAssertEqual(lineString.string, " \n \n")
XCTAssertEqual(lineString.lineNumber(at: index), 2)
XCTAssertEqual(lineString.lineRange(at: index), NSRange(location: 3, length: 3))
XCTAssertEqual(lineString.lineStartIndex(at: index), 3)
#expect(lineString.string == " \n \n")
#expect(lineString.lineNumber(at: 4) == 2)
#expect(lineString.lineRange(at: 4) == NSRange(location: 3, length: 3))
#expect(lineString.lineStartIndex(at: 4) == 3)
}
}

View File

@ -24,10 +24,11 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class LineSortTests: XCTestCase {
struct LineSortTests {
private let lines = """
dog, 🐕, 2,
@ -36,7 +37,7 @@ final class LineSortTests: XCTestCase {
"""
func testCSVSort() {
@Test func csvSort() {
var pattern = CSVSortPattern()
pattern.column = 3
@ -47,14 +48,14 @@ final class LineSortTests: XCTestCase {
cow, 🐄, 3,
"""
XCTAssertEqual(pattern.sort(self.lines), result)
XCTAssertEqual(pattern.sort(""), "")
XCTAssertNoThrow(try pattern.validate())
XCTAssertNil(pattern.range(for: "dog, 🐕, , イヌ"))
#expect(pattern.sort(self.lines) == result)
#expect(pattern.sort("").isEmpty)
#expect(throws: Never.self) { try pattern.validate() }
#expect(pattern.range(for: "dog, 🐕, , イヌ") == nil)
}
func testRegexSort() throws {
@Test func regexSort() throws {
var pattern = RegularExpressionSortPattern()
pattern.searchPattern = ", ([0-9]),"
@ -65,24 +66,24 @@ final class LineSortTests: XCTestCase {
cow, 🐄, 3,
"""
XCTAssertEqual(pattern.sort(self.lines), result)
#expect(pattern.sort(self.lines) == result)
pattern.usesCaptureGroup = true
pattern.group = 1
XCTAssertEqual(pattern.sort(self.lines), result)
XCTAssertEqual(pattern.sort(""), "")
XCTAssertNoThrow(try pattern.validate())
#expect(pattern.sort(self.lines) == result)
#expect(pattern.sort("").isEmpty)
#expect(throws: Never.self) { try pattern.validate() }
pattern.searchPattern = "\\"
XCTAssertThrowsError(try pattern.validate())
#expect(throws: SortPatternError.invalidRegularExpressionPattern) { try pattern.validate() }
pattern.searchPattern = "(a)(b)c"
try pattern.validate()
XCTAssertEqual(pattern.numberOfCaptureGroups, 2)
#expect(pattern.numberOfCaptureGroups == 2)
}
func testFuzzySort() {
@Test func fuzzySort() {
var pattern = CSVSortPattern()
pattern.column = 4
@ -96,13 +97,13 @@ final class LineSortTests: XCTestCase {
cat, 🐈, 1,
"""
XCTAssertEqual(pattern.sort(self.lines, options: options), result)
XCTAssertEqual(pattern.sort(""), "")
XCTAssertNoThrow(try pattern.validate())
#expect(pattern.sort(self.lines, options: options) == result)
#expect(pattern.sort("").isEmpty)
#expect(throws: Never.self) { try pattern.validate() }
}
func testNumericSorts() {
@Test func numericSort() {
let pattern = EntireLineSortPattern()
let numbers = """
@ -114,71 +115,71 @@ final class LineSortTests: XCTestCase {
var options = SortOptions()
options.numeric = false
XCTAssertEqual(pattern.sort(numbers, options: options), "1\n12\n3")
#expect(pattern.sort(numbers, options: options) == "1\n12\n3")
options.numeric = true
XCTAssertEqual(pattern.sort(numbers, options: options), "1\n3\n12")
#expect(pattern.sort(numbers, options: options) == "1\n3\n12")
options.descending = true
XCTAssertEqual(pattern.sort(numbers, options: options), "12\n3\n1")
#expect(pattern.sort(numbers, options: options) == "12\n3\n1")
options.descending = false
options.keepsFirstLine = true
XCTAssertEqual(pattern.sort(numbers, options: options), "3\n1\n12")
#expect(pattern.sort(numbers, options: options) == "3\n1\n12")
}
func testTargetRange() throws {
@Test func targetRange() throws {
let string = "dog"
XCTAssertEqual(EntireLineSortPattern().range(for: string), string.startIndex..<string.endIndex)
XCTAssertEqual(CSVSortPattern().range(for: string), string.startIndex..<string.endIndex)
XCTAssertNil(RegularExpressionSortPattern().range(for: string))
#expect(EntireLineSortPattern().range(for: string) == string.startIndex..<string.endIndex)
#expect(CSVSortPattern().range(for: string) == string.startIndex..<string.endIndex)
#expect(RegularExpressionSortPattern().range(for: string) == nil)
XCTAssertEqual(CSVSortPattern().range(for: ""), Range(NSRange(0..<0), in: ""))
#expect(CSVSortPattern().range(for: "") == Range(NSRange(0..<0), in: ""))
let csvString = " dog , dog cow "
var pattern = CSVSortPattern()
pattern.column = 2
XCTAssertEqual(pattern.range(for: csvString), Range(NSRange(8..<15), in: csvString))
#expect(pattern.range(for: csvString) == Range(NSRange(8..<15), in: csvString))
let tsvString = "a\tb"
pattern.column = 1
let range = try XCTUnwrap(pattern.range(for: tsvString))
XCTAssertEqual(pattern.sortKey(for: tsvString), tsvString)
XCTAssertEqual(NSRange(range, in: tsvString), NSRange(0..<3))
let range = try #require(pattern.range(for: tsvString))
#expect(pattern.sortKey(for: tsvString) == tsvString)
#expect(NSRange(range, in: tsvString) == NSRange(0..<3))
}
func testNumberParse() throws {
@Test func parseNumber() throws {
var options = SortOptions()
options.locale = .init(identifier: "en")
XCTAssertTrue(options.isLocalized)
XCTAssertTrue(options.numeric)
XCTAssertEqual(options.parse("0"), 0)
XCTAssertEqual(options.parse("10 000"), 10000)
XCTAssertEqual(options.parse("-1000.1 m/s"), -1000.1)
XCTAssertEqual(options.parse("-1000,1 m/s"), -1000)
XCTAssertEqual(options.parse("+1,000"), 1000)
XCTAssertNil(options.parse("dog 10"))
#expect(options.isLocalized)
#expect(options.numeric)
#expect(options.parse("0") == 0)
#expect(options.parse("10 000") == 10000)
#expect(options.parse("-1000.1 m/s") == -1000.1)
#expect(options.parse("-1000,1 m/s") == -1000)
#expect(options.parse("+1,000") == 1000)
#expect(options.parse("dog 10") == nil)
options.locale = .init(identifier: "de")
XCTAssertTrue(options.numeric)
XCTAssertEqual(options.parse("0"), 0)
XCTAssertEqual(options.parse("10 000"), 10000)
XCTAssertEqual(options.parse("-1000.1 m/s"), -1000)
XCTAssertEqual(options.parse("-1000,1 m/s"), -1000.1)
XCTAssertEqual(options.parse("+1,000"), 1)
XCTAssertNil(options.parse("dog 10"))
#expect(options.numeric)
#expect(options.parse("0") == 0)
#expect(options.parse("10 000") == 10000)
#expect(options.parse("-1000.1 m/s") == -1000)
#expect(options.parse("-1000,1 m/s") == -1000.1)
#expect(options.parse("+1,000") == 1)
#expect(options.parse("dog 10") == nil)
options.numeric = false
XCTAssertNil(options.parse("0"))
XCTAssertNil(options.parse("10 000"))
XCTAssertNil(options.parse("-1000.1 m/s"))
XCTAssertNil(options.parse("-1000,1 m/s"))
XCTAssertNil(options.parse("+1,000"))
XCTAssertNil(options.parse("dog 10"))
#expect(options.parse("0") == nil)
#expect(options.parse("10 000") == nil)
#expect(options.parse("-1000.1 m/s") == nil)
#expect(options.parse("-1000,1 m/s") == nil)
#expect(options.parse("+1,000") == nil)
#expect(options.parse("dog 10") == nil)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2020-2023 1024jp
// © 2020-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,69 +23,70 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class NSAttributedStringTests: XCTestCase {
struct NSAttributedStringTests {
func testAddition() throws {
@Test func add() throws {
let foo = NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"])
let bar = NSAttributedString(string: "bar", attributes: [:])
let fooBar = foo + bar
XCTAssertFalse(fooBar is NSMutableAttributedString)
XCTAssertEqual(fooBar.string, "foobar")
XCTAssertEqual(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof")
XCTAssertNil(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil))
#expect(!(fooBar is NSMutableAttributedString))
#expect(fooBar.string == "foobar")
#expect(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
#expect(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
}
func testAdditionEqual() throws {
@Test func addEqual() throws {
var fooBar = NSAttributedString(string: "foo", attributes: [.toolTip: "moof"])
fooBar += NSAttributedString(string: "bar", attributes: [:])
XCTAssertFalse(fooBar is NSMutableAttributedString)
XCTAssertEqual(fooBar.string, "foobar")
XCTAssertEqual(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof")
XCTAssertNil(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil))
#expect(!(fooBar is NSMutableAttributedString))
#expect(fooBar.string == "foobar")
#expect(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
#expect(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
}
func testTruncation() throws {
@Test func truncate() throws {
let string1 = NSMutableAttributedString(string: "0123456")
string1.truncateHead(until: 5, offset: 2)
XCTAssertEqual(string1.string, "…3456")
#expect(string1.string == "…3456")
let string2 = NSMutableAttributedString(string: "0123456")
string2.truncateHead(until: 2, offset: 3)
XCTAssertEqual(string2.string, "0123456")
#expect(string2.string == "0123456")
let string3 = NSMutableAttributedString(string: "🐱🐶🐮")
string3.truncateHead(until: 4, offset: 1)
XCTAssertEqual(string3.string, "…🐶🐮")
#expect(string3.string == "…🐶🐮")
let string4 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string4.truncateHead(until: 4, offset: 1)
XCTAssertEqual(string4.string, "🐈‍⬛🐕🐄")
#expect(string4.string == "🐈‍⬛🐕🐄")
let string5 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string5.truncateHead(until: 5, offset: 1)
XCTAssertEqual(string5.string, "🐈‍⬛🐕🐄")
#expect(string5.string == "🐈‍⬛🐕🐄")
let string6 = NSMutableAttributedString(string: "🐈⬛ab")
string6.truncateHead(until: 5, offset: 1)
XCTAssertEqual(string6.string, "…ab")
#expect(string6.string == "…ab")
let string7 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string7.truncateHead(until: 6, offset: 1)
XCTAssertEqual(string7.string, "…🐕🐄")
#expect(string7.string == "…🐕🐄")
}
func testJoin() {
@Test func join() {
let attrs: [NSAttributedString] = [
NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"]),
@ -95,26 +96,26 @@ final class NSAttributedStringTests: XCTestCase {
let space = NSAttributedString(string: " ", attributes: [.toolTip: "space"])
let joined = attrs.joined()
XCTAssertFalse(joined is NSMutableAttributedString)
XCTAssertEqual(joined.string, "foobarbuz")
XCTAssertEqual(joined.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof")
XCTAssertNil(joined.attribute(.toolTip, at: 3, effectiveRange: nil))
#expect(!(joined is NSMutableAttributedString))
#expect(joined.string == "foobarbuz")
#expect(joined.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
#expect(joined.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
let spaceJoined = attrs.joined(separator: space)
XCTAssertFalse(spaceJoined is NSMutableAttributedString)
XCTAssertEqual(spaceJoined.string, "foo bar buz")
XCTAssertEqual(spaceJoined.attribute(.toolTip, at: 0, effectiveRange: nil) as? String, "moof")
XCTAssertEqual(spaceJoined.attribute(.toolTip, at: 3, effectiveRange: nil) as? String, "space")
XCTAssertNil(spaceJoined.attribute(.toolTip, at: 4, effectiveRange: nil))
#expect(!(spaceJoined is NSMutableAttributedString))
#expect(spaceJoined.string == "foo bar buz")
#expect(spaceJoined.attribute(.toolTip, at: 0, effectiveRange: nil) as? String == "moof")
#expect(spaceJoined.attribute(.toolTip, at: 3, effectiveRange: nil) as? String == "space")
#expect(spaceJoined.attribute(.toolTip, at: 4, effectiveRange: nil) == nil)
let empty: [NSAttributedString] = []
let emptyJoined = empty.joined(separator: space)
XCTAssertFalse(emptyJoined is NSMutableAttributedString)
XCTAssertEqual(emptyJoined.string, "")
#expect(!(emptyJoined is NSMutableAttributedString))
#expect(emptyJoined.string.isEmpty)
let single: [NSAttributedString] = [NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"])]
let singleJoined = single.joined(separator: space)
XCTAssertFalse(singleJoined is NSMutableAttributedString)
XCTAssertEqual(singleJoined.string, "foo")
#expect(!(singleJoined is NSMutableAttributedString))
#expect(singleJoined.string == "foo")
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2019-2023 1024jp
// © 2019-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,107 +24,94 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class NSLayoutManagerTests: XCTestCase {
struct NSLayoutManagerTests {
func testTemporaryAttributeExistence() {
@Test func checkTemporaryAttribute() {
let layoutManager = NSLayoutManager()
let textStorage = NSTextStorage(string: "cat dog cow")
textStorage.addLayoutManager(layoutManager)
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<0)))
#expect(!layoutManager.hasTemporaryAttribute(.foregroundColor))
#expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<0)))
layoutManager.addTemporaryAttribute(.foregroundColor, value: NSColor.green, forCharacterRange: NSRange(4..<7))
layoutManager.addTemporaryAttribute(.backgroundColor, value: NSColor.white, forCharacterRange: NSRange(6..<8))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<4)))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(3..<6)))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(6..<8)))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<7)))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<textStorage.length)))
#expect(layoutManager.hasTemporaryAttribute(.foregroundColor))
#expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<4)))
#expect(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(3..<6)))
#expect(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(6..<8)))
#expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<7)))
#expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<textStorage.length)))
}
func testLeftCharacterIndex() {
@Test func characterIndex() {
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "dog\r\n 🐕")
textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 0)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 0)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 2)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 2)
// left characters
#expect(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 0)
#expect(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 0)
#expect(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 1)
#expect(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 2)
#expect(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 2)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 6)
#expect(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 3)
#expect(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 6)
// right characters
#expect(layoutManager.rightCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 1)
#expect(layoutManager.rightCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 2)
#expect(layoutManager.rightCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 3)
#expect(layoutManager.rightCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.rightCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.rightCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 6)
#expect(layoutManager.rightCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 8)
#expect(layoutManager.rightCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 8)
#expect(layoutManager.rightCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 8)
}
func testRightCharacterIndex() {
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "dog\r\n 🐕")
textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 1)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 2)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 3)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 6)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 8)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 8)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 8)
}
func testBidiLeftCharacterIndex() {
@Test func bidiCharacterIndex() {
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "كلب\r\n 🐕")
textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 2)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 0)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 1)
// left characters
#expect(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 1)
#expect(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 2)
#expect(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 3)
#expect(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 0)
#expect(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 6)
}
#expect(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 3)
#expect(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 6)
// right characters
#expect(layoutManager.rightCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 5)
#expect(layoutManager.rightCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 0)
#expect(layoutManager.rightCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 1)
#expect(layoutManager.rightCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 2)
#expect(layoutManager.rightCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 5)
func testBidiRightCharacterIndex() {
let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "كلب\r\n 🐕")
textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 0)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 1)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 2)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 5)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 6)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 8)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 8)
XCTAssertEqual(layoutManager.rightCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 8)
#expect(layoutManager.rightCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 6)
#expect(layoutManager.rightCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 8)
#expect(layoutManager.rightCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 8)
#expect(layoutManager.rightCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 8)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2023 1024jp
// © 2023-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,65 +23,66 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class NSRangeTests: XCTestCase {
struct NSRangeTests {
func testBoundaryTouch() {
@Test func touchBoundary() {
XCTAssert(NSRange(location: 2, length: 2).touches(NSRange(location: 4, length: 2)))
XCTAssert(NSRange(location: 2, length: 2).touches(NSRange(location: 0, length: 2)))
#expect(NSRange(location: 2, length: 2).touches(NSRange(location: 4, length: 2)))
#expect(NSRange(location: 2, length: 2).touches(NSRange(location: 0, length: 2)))
XCTAssert(NSRange(location: 2, length: 0).touches(NSRange(location: 2, length: 2)))
XCTAssert(NSRange(location: 2, length: 0).touches(NSRange(location: 0, length: 2)))
XCTAssert(NSRange(location: 2, length: 2).touches(NSRange(location: 2, length: 0)))
XCTAssert(NSRange(location: 2, length: 2).touches(NSRange(location: 4, length: 0)))
#expect(NSRange(location: 2, length: 0).touches(NSRange(location: 2, length: 2)))
#expect(NSRange(location: 2, length: 0).touches(NSRange(location: 0, length: 2)))
#expect(NSRange(location: 2, length: 2).touches(NSRange(location: 2, length: 0)))
#expect(NSRange(location: 2, length: 2).touches(NSRange(location: 4, length: 0)))
XCTAssert(NSRange(location: 2, length: 2).touches(2))
XCTAssert(NSRange(location: 2, length: 2).touches(4))
#expect(NSRange(location: 2, length: 2).touches(2))
#expect(NSRange(location: 2, length: 2).touches(4))
}
func testNotFound() {
@Test func notFound() {
XCTAssert(NSRange.notFound.isNotFound)
XCTAssert(NSRange.notFound.isEmpty)
XCTAssert(NSRange(location: NSNotFound, length: 1).isNotFound)
XCTAssertFalse(NSRange(location: 1, length: 1).isNotFound)
#expect(NSRange.notFound.isNotFound)
#expect(NSRange.notFound.isEmpty)
#expect(NSRange(location: NSNotFound, length: 1).isNotFound)
#expect(!NSRange(location: 1, length: 1).isNotFound)
}
func testRangeInsertion() {
@Test func insertRange() {
XCTAssertEqual(NSRange(0..<0).inserted(items: []), NSRange(0..<0))
XCTAssertEqual(NSRange(0..<0).inserted(items: [.init(string: "", location: 0, forward: true)]), NSRange(0..<0))
#expect(NSRange(0..<0).inserted(items: []) == NSRange(0..<0))
#expect(NSRange(0..<0).inserted(items: [.init(string: "", location: 0, forward: true)]) == NSRange(0..<0))
XCTAssertEqual(NSRange(0..<0).inserted(items: [.init(string: "abc", location: 0, forward: true)]), NSRange(3..<3))
XCTAssertEqual(NSRange(0..<0).inserted(items: [.init(string: "abc", location: 0, forward: false)]), NSRange(0..<0))
XCTAssertEqual(NSRange(1..<1).inserted(items: [.init(string: "abc", location: 0, forward: false)]), NSRange(4..<4))
XCTAssertEqual(NSRange(0..<5).inserted(items: [.init(string: "abc", location: 2, forward: true)]), NSRange(0..<8))
XCTAssertEqual(NSRange(0..<5).inserted(items: [.init(string: "abc", location: 6, forward: true)]), NSRange(0..<5))
#expect(NSRange(0..<0).inserted(items: [.init(string: "abc", location: 0, forward: true)]) == NSRange(3..<3))
#expect(NSRange(0..<0).inserted(items: [.init(string: "abc", location: 0, forward: false)]) == NSRange(0..<0))
#expect(NSRange(1..<1).inserted(items: [.init(string: "abc", location: 0, forward: false)]) == NSRange(4..<4))
#expect(NSRange(0..<5).inserted(items: [.init(string: "abc", location: 2, forward: true)]) == NSRange(0..<8))
#expect(NSRange(0..<5).inserted(items: [.init(string: "abc", location: 6, forward: true)]) == NSRange(0..<5))
XCTAssertEqual(NSRange(2..<2).inserted(items: [.init(string: "abc", location: 2, forward: true),
.init(string: "abc", location: 2, forward: false)]), NSRange(5..<5))
XCTAssertEqual(NSRange(2..<3).inserted(items: [.init(string: "abc", location: 2, forward: true),
.init(string: "abc", location: 2, forward: false)]), NSRange(2..<6))
XCTAssertEqual(NSRange(2..<3).inserted(items: [.init(string: "abc", location: 3, forward: true),
.init(string: "abc", location: 3, forward: false)]), NSRange(2..<6))
#expect(NSRange(2..<2).inserted(items: [.init(string: "abc", location: 2, forward: true),
.init(string: "abc", location: 2, forward: false)]) == NSRange(5..<5))
#expect(NSRange(2..<3).inserted(items: [.init(string: "abc", location: 2, forward: true),
.init(string: "abc", location: 2, forward: false)]) == NSRange(2..<6))
#expect(NSRange(2..<3).inserted(items: [.init(string: "abc", location: 3, forward: true),
.init(string: "abc", location: 3, forward: false)]) == NSRange(2..<6))
}
func testRangeRemoval() {
@Test func removeRange() {
XCTAssertEqual(NSRange(0..<0).removed(ranges: []), NSRange(0..<0))
XCTAssertEqual(NSRange(0..<0).removed(ranges: [NSRange(0..<0)]), NSRange(0..<0))
#expect(NSRange(0..<0).removed(ranges: []) == NSRange(0..<0))
#expect(NSRange(0..<0).removed(ranges: [NSRange(0..<0)]) == NSRange(0..<0))
XCTAssertEqual(NSRange(0..<10).removed(ranges: [NSRange(2..<4)]), NSRange(0..<8))
XCTAssertEqual(NSRange(1..<10).removed(ranges: [NSRange(0..<2)]), NSRange(0..<8))
XCTAssertEqual(NSRange(1..<10).removed(ranges: [NSRange(11..<20)]), NSRange(1..<10))
#expect(NSRange(0..<10).removed(ranges: [NSRange(2..<4)]) == NSRange(0..<8))
#expect(NSRange(1..<10).removed(ranges: [NSRange(0..<2)]) == NSRange(0..<8))
#expect(NSRange(1..<10).removed(ranges: [NSRange(11..<20)]) == NSRange(1..<10))
XCTAssertEqual(NSRange(1..<10).removed(ranges: [NSRange(2..<4), NSRange(3..<5)]), NSRange(1..<7))
XCTAssertEqual(NSRange(1..<10).removed(ranges: [NSRange(0..<2), NSRange(3..<5), NSRange(9..<20)]), NSRange(0..<5))
#expect(NSRange(1..<10).removed(ranges: [NSRange(2..<4), NSRange(3..<5)]) == NSRange(1..<7))
#expect(NSRange(1..<10).removed(ranges: [NSRange(0..<2), NSRange(3..<5), NSRange(9..<20)]) == NSRange(0..<5))
}
}

View File

@ -23,10 +23,11 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class OutlineTests: XCTestCase {
struct OutlineTests {
private let items: [OutlineItem] = [
OutlineItem(title: "dog", range: NSRange(location: 10, length: 5)), // 0
@ -42,49 +43,49 @@ final class OutlineTests: XCTestCase {
func testIndex() throws {
@Test func index() throws {
XCTAssertNil(self.emptyItems.item(at: 10))
#expect(self.emptyItems.item(at: 10) == nil)
XCTAssertNil(self.items.item(at: 9))
XCTAssertEqual(self.items.item(at: 10), self.items[0])
XCTAssertEqual(self.items.item(at: 18), self.items[0])
XCTAssertEqual(self.items.item(at: 20), self.items[0])
XCTAssertEqual(self.items.item(at: 40), self.items[3])
XCTAssertEqual(self.items.item(at: 50), self.items[3])
XCTAssertEqual(self.items.item(at: 59), self.items[3])
XCTAssertEqual(self.items.item(at: 60), self.items[5])
#expect(self.items.item(at: 9) == nil)
#expect(self.items.item(at: 10) == self.items[0])
#expect(self.items.item(at: 18) == self.items[0])
#expect(self.items.item(at: 20) == self.items[0])
#expect(self.items.item(at: 40) == self.items[3])
#expect(self.items.item(at: 50) == self.items[3])
#expect(self.items.item(at: 59) == self.items[3])
#expect(self.items.item(at: 60) == self.items[5])
}
func testPreviousItem() throws {
@Test func previousItem() throws {
XCTAssertNil(self.emptyItems.previousItem(for: NSRange(10..<20)))
#expect(self.emptyItems.previousItem(for: NSRange(10..<20)) == nil)
XCTAssertNil(self.items.previousItem(for: NSRange(10..<20)))
XCTAssertNil(self.items.previousItem(for: NSRange(19..<19)))
XCTAssertEqual(self.items.previousItem(for: NSRange(59..<70)), items[0])
XCTAssertEqual(self.items.previousItem(for: NSRange(60..<70)), items[3])
#expect(self.items.previousItem(for: NSRange(10..<20)) == nil)
#expect(self.items.previousItem(for: NSRange(19..<19)) == nil)
#expect(self.items.previousItem(for: NSRange(59..<70)) == items[0])
#expect(self.items.previousItem(for: NSRange(60..<70)) == items[3])
}
func testNextItem() throws {
@Test func nextItem() throws {
XCTAssertNil(self.emptyItems.nextItem(for: NSRange(10..<20)))
#expect(self.emptyItems.nextItem(for: NSRange(10..<20)) == nil)
XCTAssertEqual(self.items.nextItem(for: NSRange(0..<0)), items[0])
XCTAssertEqual(self.items.nextItem(for: NSRange(0..<10)), items[3])
XCTAssertEqual(self.items.nextItem(for: NSRange(40..<40)), items[5])
XCTAssertNil(self.items.nextItem(for: NSRange(60..<60)))
XCTAssertNil(self.items.nextItem(for: NSRange(40..<61)))
#expect(self.items.nextItem(for: NSRange(0..<0)) == items[0])
#expect(self.items.nextItem(for: NSRange(0..<10)) == items[3])
#expect(self.items.nextItem(for: NSRange(40..<40)) == items[5])
#expect(self.items.nextItem(for: NSRange(60..<60)) == nil)
#expect(self.items.nextItem(for: NSRange(40..<61)) == nil)
}
func testFilter() throws {
@Test func filter() throws {
XCTAssertEqual(self.items.compactMap { $0.filter("", keyPath: \.title) }.count, 7)
XCTAssertEqual(self.items.compactMap { $0.filter("cat", keyPath: \.title) }.count, 0)
XCTAssertEqual(self.items.compactMap { $0.filter("dog", keyPath: \.title) }.count, 2)
XCTAssertEqual(self.items.compactMap { $0.filter("dow", keyPath: \.title) }.count, 1)
#expect(self.items.compactMap { $0.filter("", keyPath: \.title) }.count == 7)
#expect(self.items.compactMap { $0.filter("cat", keyPath: \.title) }.count == 0)
#expect(self.items.compactMap { $0.filter("dog", keyPath: \.title) }.count == 2)
#expect(self.items.compactMap { $0.filter("dow", keyPath: \.title) }.count == 1)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2022-2023 1024jp
// © 2022-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,63 +23,67 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class RegularExpressionSyntaxTests: XCTestCase {
struct RegularExpressionSyntaxTests {
func testBracketHighlight() throws {
@Test func highlightBracket() throws {
// -> Only the `]` at the first position will be evaluated as a character.
let character = RegularExpressionSyntaxType.character
XCTAssertEqual(character.ranges(in: "[abc]"), [NSRange(location: 1, length: 3)])
XCTAssertEqual(character.ranges(in: "\\[a[a]"), [NSRange(location: 0, length: 2), NSRange(location: 4, length: 1)])
XCTAssertEqual(character.ranges(in: "[a\\]]"), [NSRange(location: 2, length: 2), NSRange(location: 1, length: 3)])
XCTAssertEqual(character.ranges(in: "[]]"), [NSRange(location: 1, length: 1)])
XCTAssertEqual(character.ranges(in: "[a]]"), [NSRange(location: 1, length: 1)])
XCTAssertEqual(character.ranges(in: "[]a]"), [NSRange(location: 1, length: 2)])
XCTAssertEqual(character.ranges(in: "[a]b]"), [NSRange(location: 1, length: 1)])
#expect(character.ranges(in: "[abc]") == [NSRange(location: 1, length: 3)])
#expect(character.ranges(in: "\\[a[a]") == [NSRange(location: 0, length: 2), NSRange(location: 4, length: 1)])
#expect(character.ranges(in: "[a\\]]") == [NSRange(location: 2, length: 2), NSRange(location: 1, length: 3)])
#expect(character.ranges(in: "[]]") == [NSRange(location: 1, length: 1)])
#expect(character.ranges(in: "[a]]") == [NSRange(location: 1, length: 1)])
#expect(character.ranges(in: "[]a]") == [NSRange(location: 1, length: 2)])
#expect(character.ranges(in: "[a]b]") == [NSRange(location: 1, length: 1)])
XCTAssertEqual(character.ranges(in: "[a] [b]"), [NSRange(location: 1, length: 1),
#expect(character.ranges(in: "[a] [b]") == [NSRange(location: 1, length: 1),
NSRange(location: 5, length: 1)])
XCTAssertEqual(character.ranges(in: "[^a]"), [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^^]"), [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]]"), [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]]]"), [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^a]]"), [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]a]"), [NSRange(location: 2, length: 2)])
XCTAssertEqual(character.ranges(in: "[^a]b]"), [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^a]") == [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^^]") == [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^]]") == [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^]]]") == [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^a]]") == [NSRange(location: 2, length: 1)])
#expect(character.ranges(in: "[^]a]") == [NSRange(location: 2, length: 2)])
#expect(character.ranges(in: "[^a]b]") == [NSRange(location: 2, length: 1)])
// just containing ranges for `\[`
XCTAssertEqual(character.ranges(in: "(?<=\\[)a]"), [NSRange(location: 4, length: 2)])
#expect(character.ranges(in: "(?<=\\[)a]") == [NSRange(location: 4, length: 2)])
}
@Test func highlightSymbol() {
let symbol = RegularExpressionSyntaxType.symbol
XCTAssertEqual(symbol.ranges(in: "[abc]"), [NSRange(location: 0, length: 5)])
XCTAssertEqual(symbol.ranges(in: "\\[a[a]"), [NSRange(location: 3, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[a\\]]"), [NSRange(location: 0, length: 5)])
XCTAssertEqual(symbol.ranges(in: "[]]"), [NSRange(location: 0, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[a]]"), [NSRange(location: 0, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[]a]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[a]b]"), [NSRange(location: 0, length: 3)])
#expect(symbol.ranges(in: "[abc]") == [NSRange(location: 0, length: 5)])
#expect(symbol.ranges(in: "\\[a[a]") == [NSRange(location: 3, length: 3)])
#expect(symbol.ranges(in: "[a\\]]") == [NSRange(location: 0, length: 5)])
#expect(symbol.ranges(in: "[]]") == [NSRange(location: 0, length: 3)])
#expect(symbol.ranges(in: "[a]]") == [NSRange(location: 0, length: 3)])
#expect(symbol.ranges(in: "[]a]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[a]b]") == [NSRange(location: 0, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[a] [b]"), [NSRange(location: 0, length: 3),
#expect(symbol.ranges(in: "[a] [b]") == [NSRange(location: 0, length: 3),
NSRange(location: 4, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[^a]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^^]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]]]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^a]]"), [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]a]"), [NSRange(location: 0, length: 5)])
XCTAssertEqual(symbol.ranges(in: "[^a]b]"), [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^a]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^^]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^]]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^]]]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^a]]") == [NSRange(location: 0, length: 4)])
#expect(symbol.ranges(in: "[^]a]") == [NSRange(location: 0, length: 5)])
#expect(symbol.ranges(in: "[^a]b]") == [NSRange(location: 0, length: 4)])
// just containing ranges for `(?<=`, `(` and `)`
XCTAssertEqual(symbol.ranges(in: "(?<=\\[)a]"), [NSRange(location: 0, length: 4),
#expect(symbol.ranges(in: "(?<=\\[)a]") == [NSRange(location: 0, length: 4),
NSRange(location: 0, length: 1),
NSRange(location: 6, length: 1)])
}

View File

@ -23,59 +23,64 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class ShiftJISTests: XCTestCase {
struct ShiftJISTests {
func testIANACharSetNames() {
@Test func ianaCharSetNames() {
XCTAssertEqual(ShiftJIS.shiftJIS.ianaCharSet, "shift_jis")
XCTAssertEqual(ShiftJIS.shiftJIS_X0213.ianaCharSet, "Shift_JIS")
XCTAssertEqual(ShiftJIS.macJapanese.ianaCharSet, "x-mac-japanese")
XCTAssertEqual(ShiftJIS.dosJapanese.ianaCharSet, "cp932")
#expect(ShiftJIS.shiftJIS.ianaCharSet == "shift_jis")
#expect(ShiftJIS.shiftJIS_X0213.ianaCharSet == "Shift_JIS")
#expect(ShiftJIS.macJapanese.ianaCharSet == "x-mac-japanese")
#expect(ShiftJIS.dosJapanese.ianaCharSet == "cp932")
XCTAssertEqual(ShiftJIS(ianaCharSetName: ShiftJIS.shiftJIS.ianaCharSet!), .shiftJIS)
XCTAssertEqual(ShiftJIS(ianaCharSetName: ShiftJIS.shiftJIS_X0213.ianaCharSet!), .shiftJIS)
#expect(ShiftJIS(ianaCharSetName: ShiftJIS.shiftJIS.ianaCharSet!) == .shiftJIS)
#expect(ShiftJIS(ianaCharSetName: ShiftJIS.shiftJIS_X0213.ianaCharSet!) == .shiftJIS)
}
func testTildaEncoding() {
@Test func encodeTilda() {
XCTAssertEqual(ShiftJIS.shiftJIS.encode("~"), "?")
XCTAssertEqual(ShiftJIS.shiftJIS_X0213.encode("~"), "")
XCTAssertEqual(ShiftJIS.macJapanese.encode("~"), "~")
XCTAssertEqual(ShiftJIS.dosJapanese.encode("~"), "~")
#expect(ShiftJIS.shiftJIS.encode("~") == "?")
#expect(ShiftJIS.shiftJIS_X0213.encode("~") == "")
#expect(ShiftJIS.macJapanese.encode("~") == "~")
#expect(ShiftJIS.dosJapanese.encode("~") == "~")
}
func testBackslashEncoding() {
@Test func encodeBackslash() {
XCTAssertEqual(ShiftJIS.shiftJIS.encode("\\"), "")
XCTAssertEqual(ShiftJIS.shiftJIS_X0213.encode("\\"), "")
XCTAssertEqual(ShiftJIS.macJapanese.encode("\\"), "\\")
XCTAssertEqual(ShiftJIS.dosJapanese.encode("\\"), "\\")
#expect(ShiftJIS.shiftJIS.encode("\\") == "")
#expect(ShiftJIS.shiftJIS_X0213.encode("\\") == "")
#expect(ShiftJIS.macJapanese.encode("\\") == "\\")
#expect(ShiftJIS.dosJapanese.encode("\\") == "\\")
}
func testYenEncoding() {
@Test func encodeYen() {
XCTAssertEqual(ShiftJIS.shiftJIS.encode("¥"), "¥")
XCTAssertEqual(ShiftJIS.shiftJIS_X0213.encode("¥"), "¥")
XCTAssertEqual(ShiftJIS.macJapanese.encode("¥"), "¥")
XCTAssertEqual(ShiftJIS.dosJapanese.encode("¥"), "?")
#expect(ShiftJIS.shiftJIS.encode("¥") == "¥")
#expect(ShiftJIS.shiftJIS_X0213.encode("¥") == "¥")
#expect(ShiftJIS.macJapanese.encode("¥") == "¥")
#expect(ShiftJIS.dosJapanese.encode("¥") == "?")
}
func testYenConversion() {
@Test func convertYen() {
XCTAssertEqual("¥".convertYenSign(for: ShiftJIS.shiftJIS.encoding), "¥")
XCTAssertEqual("¥".convertYenSign(for: ShiftJIS.shiftJIS_X0213.encoding), "¥")
XCTAssertEqual("¥".convertYenSign(for: ShiftJIS.macJapanese.encoding), "¥")
XCTAssertEqual("¥".convertYenSign(for: ShiftJIS.dosJapanese.encoding), "\\")
#expect("¥".convertYenSign(for: ShiftJIS.shiftJIS.encoding) == "¥")
#expect("¥".convertYenSign(for: ShiftJIS.shiftJIS_X0213.encoding) == "¥")
#expect("¥".convertYenSign(for: ShiftJIS.macJapanese.encoding) == "¥")
#expect("¥".convertYenSign(for: ShiftJIS.dosJapanese.encoding) == "\\")
}
ShiftJIS.allCases
.forEach { XCTAssertEqual("¥".convertYenSign(for: $0.encoding) == "¥", $0.encode("¥") == "¥") }
@Test(arguments: ShiftJIS.allCases)
private func convertYen(shiftJIS: ShiftJIS) {
#expect(("¥".convertYenSign(for: shiftJIS.encoding) == "¥") == (shiftJIS.encode("¥") == "¥"))
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2016-2023 1024jp
// © 2016-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,94 +24,95 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class ShortcutTests: XCTestCase {
struct ShortcutTests {
func testEquivalent() {
@Test func equivalent() {
XCTAssertEqual(Shortcut("A", modifiers: [.control]),
#expect(Shortcut("A", modifiers: [.control]) ==
Shortcut("a", modifiers: [.control, .shift]))
XCTAssertEqual(Shortcut(keySpecChars: "^A"),
#expect(Shortcut(keySpecChars: "^A") ==
Shortcut(keySpecChars: "^$a"))
}
func testKeySpecCharsCreation() {
@Test func createKeySpecChars() {
XCTAssertNil(Shortcut("", modifiers: []))
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars, "^$a")
XCTAssertEqual(Shortcut("b", modifiers: [.command, .option])?.keySpecChars, "~@b")
XCTAssertEqual(Shortcut("A", modifiers: [.control])?.keySpecChars, "^$a") // uppercase for Shift key
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars, "^$a")
#expect(Shortcut("", modifiers: []) == nil)
#expect(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars == "^$a")
#expect(Shortcut("b", modifiers: [.command, .option])?.keySpecChars == "~@b")
#expect(Shortcut("A", modifiers: [.control])?.keySpecChars == "^$a") // uppercase for Shift key
#expect(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars == "^$a")
XCTAssertEqual(Shortcut("a", modifiers: [])?.keySpecChars, "a")
XCTAssertEqual(Shortcut("a", modifiers: [])?.isValid, false)
XCTAssertNil(Shortcut("", modifiers: [.control, .shift]))
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.isValid, true)
XCTAssertEqual(Shortcut("ab", modifiers: [.control, .shift])?.isValid, false)
#expect(Shortcut("a", modifiers: [])?.keySpecChars == "a")
#expect(Shortcut("a", modifiers: [])?.isValid == false)
#expect(Shortcut("", modifiers: [.control, .shift]) == nil)
#expect(Shortcut("a", modifiers: [.control, .shift])?.isValid == true)
#expect(Shortcut("ab", modifiers: [.control, .shift])?.isValid == false)
}
func testStringToShortcut() throws {
@Test func stringToShortcut() throws {
let shortcut = try XCTUnwrap(Shortcut(keySpecChars: "^$a"))
let shortcut = try #require(Shortcut(keySpecChars: "^$a"))
XCTAssertEqual(shortcut.keyEquivalent, "a")
XCTAssertEqual(shortcut.modifiers, [.control, .shift])
XCTAssert(shortcut.isValid)
#expect(shortcut.keyEquivalent == "a")
#expect(shortcut.modifiers == [.control, .shift])
#expect(shortcut.isValid)
}
func testShortcutWithFnKey() throws {
@Test func shortcutWithFnKey() throws {
let shortcut = try XCTUnwrap(Shortcut("a", modifiers: [.function]))
let shortcut = try #require(Shortcut("a", modifiers: [.function]))
XCTAssertFalse(shortcut.isValid)
XCTAssertEqual(shortcut.keyEquivalent, "a")
XCTAssertEqual(shortcut.modifiers, [.function])
XCTAssert(shortcut.symbol == "fnA" || shortcut.symbol == "🌐A")
XCTAssertEqual(shortcut.keySpecChars, "a", "The fn key should be ignored.")
#expect(!shortcut.isValid)
#expect(shortcut.keyEquivalent == "a")
#expect(shortcut.modifiers == [.function])
#expect(shortcut.symbol == "fnA" || shortcut.symbol == "🌐A")
#expect(shortcut.keySpecChars == "a", "The fn key should be ignored.")
let symbolName = try XCTUnwrap(shortcut.modifierSymbolNames.first)
XCTAssertNotNil(NSImage(systemSymbolName: symbolName, accessibilityDescription: nil))
let symbolName = try #require(shortcut.modifierSymbolNames.first)
#expect(NSImage(systemSymbolName: symbolName, accessibilityDescription: nil) != nil)
}
func testMenuItemShortcut() {
@Test func menuItemShortcut() {
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "C")
menuItem.keyEquivalentModifierMask = [.command]
let shortcut = Shortcut(menuItem.keyEquivalent, modifiers: menuItem.keyEquivalentModifierMask)
XCTAssertEqual(shortcut?.symbol, "C")
XCTAssertEqual(shortcut, menuItem.shortcut)
#expect(shortcut?.symbol == "C")
#expect(shortcut == menuItem.shortcut)
}
func testShortcutSymbols() throws {
@Test func shortcutSymbols() throws {
// test modifier symbols
XCTAssertNil(Shortcut(keySpecChars: ""))
XCTAssertEqual(Shortcut(keySpecChars: "^$a")?.symbol, "^A")
XCTAssertEqual(Shortcut(keySpecChars: "~@b")?.symbol, "B")
#expect(Shortcut(keySpecChars: "") == nil)
#expect(Shortcut(keySpecChars: "^$a")?.symbol == "^A")
#expect(Shortcut(keySpecChars: "~@b")?.symbol == "B")
// test unprintable keys
let f10 = try XCTUnwrap(String(NSEvent.SpecialKey.f10.unicodeScalar))
XCTAssertEqual(Shortcut(keySpecChars: "@" + f10)?.symbol, "F10")
let f10 = try #require(String(NSEvent.SpecialKey.f10.unicodeScalar))
#expect(Shortcut(keySpecChars: "@" + f10)?.symbol == "F10")
let delete = try XCTUnwrap(UnicodeScalar(NSDeleteCharacter).flatMap(String.init))
XCTAssertEqual(Shortcut(keySpecChars: "@" + delete)?.symbol, "⌘ ⌫")
let delete = try #require(UnicodeScalar(NSDeleteCharacter).flatMap(String.init))
#expect(Shortcut(keySpecChars: "@" + delete)?.symbol == "⌘ ⌫")
// test creation
let deleteForward = try XCTUnwrap(String(NSEvent.SpecialKey.deleteForward.unicodeScalar))
XCTAssertNil(Shortcut(symbolRepresentation: ""))
XCTAssertEqual(Shortcut(symbolRepresentation: "^A")?.keySpecChars, "^$a")
XCTAssertEqual(Shortcut(symbolRepresentation: "B")?.keySpecChars, "~@b")
XCTAssertEqual(Shortcut(symbolRepresentation: "F10")?.keySpecChars, "@" + f10)
XCTAssertEqual(Shortcut(symbolRepresentation: "⌘ ⌦")?.keySpecChars, "@" + deleteForward)
let deleteForward = try #require(String(NSEvent.SpecialKey.deleteForward.unicodeScalar))
#expect(Shortcut(symbolRepresentation: "") == nil)
#expect(Shortcut(symbolRepresentation: "^A")?.keySpecChars == "^$a")
#expect(Shortcut(symbolRepresentation: "B")?.keySpecChars == "~@b")
#expect(Shortcut(symbolRepresentation: "F10")?.keySpecChars == "@" + f10)
#expect(Shortcut(symbolRepresentation: "⌘ ⌦")?.keySpecChars == "@" + deleteForward)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2022-2023 1024jp
// © 2022-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,22 +23,23 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class SnippetTests: XCTestCase {
struct SnippetTests {
func testSimpleSnippet() {
@Test func simpleSnippet() {
let snippet = Snippet(name: "", format: "<h1><<<SELECTION>>><<<CURSOR>>></h1>")
let (string, selections) = snippet.insertion(selectedString: "abc")
XCTAssertEqual(string, "<h1>abc</h1>")
XCTAssertEqual(selections, [NSRange(location: 7, length: 0)])
#expect(string == "<h1>abc</h1>")
#expect(selections == [NSRange(location: 7, length: 0)])
}
func testMultipleLines() {
@Test func multipleLines() {
let format = """
<ul>
@ -55,8 +56,8 @@ final class SnippetTests: XCTestCase {
<li></li>
</ul>
"""
XCTAssertEqual(string, expectedString)
XCTAssertEqual(selections, [NSRange(location: 13, length: 0),
#expect(string == expectedString)
#expect(selections == [NSRange(location: 13, length: 0),
NSRange(location: 27, length: 0)])
let (indentedString, indentedSelections) = snippet.insertion(selectedString: "", indent: " ")
@ -67,13 +68,13 @@ final class SnippetTests: XCTestCase {
<li></li>
</ul>
"""
XCTAssertEqual(indentedString, expectedIndentString)
XCTAssertEqual(indentedSelections, [NSRange(location: 17, length: 0),
#expect(indentedString == expectedIndentString)
#expect(indentedSelections == [NSRange(location: 17, length: 0),
NSRange(location: 35, length: 0)])
}
func testMultipleInsertions() {
@Test func multipleInsertions() {
let string = """
aaa
@ -95,7 +96,7 @@ final class SnippetTests: XCTestCase {
let expectedSelections = [NSRange(location: 11, length: 0),
NSRange(location: 21, length: 0),
NSRange(location: 33, length: 0)]
XCTAssertEqual(strings, expectedStrings)
XCTAssertEqual(selections, expectedSelections)
#expect(strings == expectedStrings)
#expect(selections == expectedSelections)
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2017-2020 1024jp
// © 2017-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,21 +24,21 @@
// limitations under the License.
//
import XCTest
import Testing
@testable import CotEditor
final class StringCollectionTests: XCTestCase {
struct StringCollectionTests {
func testAvailableNameCreation() {
@Test func createAvailableNames() {
let names = ["foo", "foo 3", "foo copy 3", "foo 4", "foo 7"]
let copy = "copy"
XCTAssertEqual(names.createAvailableName(for: "foo"), "foo 2")
XCTAssertEqual(names.createAvailableName(for: "foo 3"), "foo 5")
#expect(names.createAvailableName(for: "foo") == "foo 2")
#expect(names.createAvailableName(for: "foo 3") == "foo 5")
XCTAssertEqual(names.createAvailableName(for: "foo", suffix: copy), "foo copy")
XCTAssertEqual(names.createAvailableName(for: "foo 3", suffix: copy), "foo 3 copy")
XCTAssertEqual(names.createAvailableName(for: "foo copy 3", suffix: copy), "foo copy 4")
#expect(names.createAvailableName(for: "foo", suffix: copy) == "foo copy")
#expect(names.createAvailableName(for: "foo 3", suffix: copy) == "foo 3 copy")
#expect(names.createAvailableName(for: "foo copy 3", suffix: copy) == "foo copy 4")
}
}

View File

@ -24,117 +24,118 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class StringCommentingTests: XCTestCase {
struct StringCommentingTests {
// MARK: String extension Tests
func testInlineCommentOut() {
@Test func inlineCommentOut() {
XCTAssertEqual("foo".inlineCommentOut(delimiter: "//", ranges: []), [])
#expect("foo".inlineCommentOut(delimiter: "//", ranges: []).isEmpty)
XCTAssertEqual("foo".inlineCommentOut(delimiter: "//", ranges: [NSRange(0..<0)]),
#expect("foo".inlineCommentOut(delimiter: "//", ranges: [NSRange(0..<0)]) ==
[.init(string: "//", location: 0, forward: true)])
XCTAssertEqual("foo".inlineCommentOut(delimiter: "//", ranges: [NSRange(1..<2)]),
#expect("foo".inlineCommentOut(delimiter: "//", ranges: [NSRange(1..<2)]) ==
[.init(string: "//", location: 1, forward: true)])
}
func testBlockCommentOut() {
@Test func blockCommentOut() {
XCTAssertEqual("foo".blockCommentOut(delimiters: Pair("<-", "->"), ranges: []), [])
#expect("foo".blockCommentOut(delimiters: Pair("<-", "->"), ranges: []).isEmpty)
XCTAssertEqual("foo".blockCommentOut(delimiters: Pair("<-", "->"), ranges: [NSRange(0..<0)]),
#expect("foo".blockCommentOut(delimiters: Pair("<-", "->"), ranges: [NSRange(0..<0)]) ==
[.init(string: "<-", location: 0, forward: true), .init(string: "->", location: 0, forward: false)])
}
func testInlineUncomment() {
@Test func inlineUncomment() {
XCTAssertEqual("foo".rangesOfInlineDelimiter("//", ranges: []), [])
XCTAssertEqual("foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<0)]), [])
#expect("foo".rangesOfInlineDelimiter("//", ranges: [])?.isEmpty == true)
#expect("foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<0)])?.isEmpty == true)
XCTAssertEqual("//foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<5)]), [NSRange(0..<2)])
XCTAssertEqual("// foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<5)]), [NSRange(0..<2)])
#expect("//foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<5)]) == [NSRange(0..<2)])
#expect("// foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<5)]) == [NSRange(0..<2)])
XCTAssertEqual(" //foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<7)]), [NSRange(2..<4)])
#expect(" //foo".rangesOfInlineDelimiter("//", ranges: [NSRange(0..<7)]) == [NSRange(2..<4)])
}
func testBlockUncomment() {
@Test func blockUncomment() {
XCTAssertEqual("foo".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: []), [])
XCTAssertEqual("foo".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<0)]), [])
#expect("foo".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [])?.isEmpty == true)
#expect("foo".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<0)])?.isEmpty == true)
XCTAssertEqual("<-foo->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<7)]), [NSRange(0..<2), NSRange(5..<7)])
XCTAssertEqual("<- foo ->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<9)]), [NSRange(0..<2), NSRange(7..<9)])
#expect("<-foo->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<7)]) == [NSRange(0..<2), NSRange(5..<7)])
#expect("<- foo ->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<9)]) == [NSRange(0..<2), NSRange(7..<9)])
XCTAssertEqual(" <-foo-> ".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<9)]), [NSRange(1..<3), NSRange(6..<8)])
XCTAssertNil(" <-foo-> ".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(1..<7)]))
#expect(" <-foo-> ".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<9)]) == [NSRange(1..<3), NSRange(6..<8)])
#expect(" <-foo-> ".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(1..<7)]) == nil)
// ok, this is currently in spec, but not a good one...
XCTAssertEqual("<-foo-><-bar->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<14)]), [NSRange(0..<2), NSRange(12..<14)])
#expect("<-foo-><-bar->".rangesOfBlockDelimiters(Pair("<-", "->"), ranges: [NSRange(0..<14)]) == [NSRange(0..<2), NSRange(12..<14)])
}
// MARK: TextView extension Tests
@MainActor func testTextViewInlineComment() {
@MainActor @Test func textViewInlineComment() {
let textView = CommentingTextView()
textView.string = "foo\nbar"
textView.selectedRanges = [NSRange(0..<3), NSRange(4..<7)] as [NSValue]
textView.commentOut(types: .inline, fromLineHead: true)
XCTAssertEqual(textView.string, "//foo\n//bar")
XCTAssertEqual(textView.selectedRanges, [NSRange(0..<5), NSRange(6..<11)] as [NSValue])
XCTAssertTrue(textView.canUncomment(partly: false))
#expect(textView.string == "//foo\n//bar")
#expect(textView.selectedRanges == [NSRange(0..<5), NSRange(6..<11)] as [NSValue])
#expect(textView.canUncomment(partly: false))
textView.uncomment()
XCTAssertEqual(textView.string, "foo\nbar")
XCTAssertEqual(textView.selectedRanges, [NSRange(0..<3), NSRange(4..<7)] as [NSValue])
#expect(textView.string == "foo\nbar")
#expect(textView.selectedRanges == [NSRange(0..<3), NSRange(4..<7)] as [NSValue])
textView.selectedRanges = [NSRange(1..<1)] as [NSValue]
textView.insertionLocations = [5]
textView.commentOut(types: .inline, fromLineHead: true)
XCTAssertEqual(textView.string, "//foo\n//bar")
XCTAssertEqual(textView.rangesForUserTextChange, [NSRange(3..<3), NSRange(9..<9)] as [NSValue])
XCTAssertTrue(textView.canUncomment(partly: false))
#expect(textView.string == "//foo\n//bar")
#expect(textView.rangesForUserTextChange == [NSRange(3..<3), NSRange(9..<9)] as [NSValue])
#expect(textView.canUncomment(partly: false))
textView.uncomment()
XCTAssertEqual(textView.string, "foo\nbar")
XCTAssertEqual(textView.rangesForUserTextChange, [NSRange(1..<1), NSRange(5..<5)] as [NSValue])
#expect(textView.string == "foo\nbar")
#expect(textView.rangesForUserTextChange == [NSRange(1..<1), NSRange(5..<5)] as [NSValue])
}
@MainActor func testTextViewBlockComment() {
@MainActor @Test func textViewBlockComment() {
let textView = CommentingTextView()
textView.string = "foo\nbar"
textView.selectedRanges = [NSRange(0..<3), NSRange(4..<7)] as [NSValue]
textView.commentOut(types: .block, fromLineHead: true)
XCTAssertEqual(textView.string, "<-foo->\n<-bar->")
XCTAssertEqual(textView.selectedRanges, [NSRange(0..<7), NSRange(8..<15)] as [NSValue])
XCTAssertTrue(textView.canUncomment(partly: false))
#expect(textView.string == "<-foo->\n<-bar->")
#expect(textView.selectedRanges == [NSRange(0..<7), NSRange(8..<15)] as [NSValue])
#expect(textView.canUncomment(partly: false))
textView.uncomment()
XCTAssertEqual(textView.string, "foo\nbar")
XCTAssertEqual(textView.selectedRanges, [NSRange(0..<3), NSRange(4..<7)] as [NSValue])
#expect(textView.string == "foo\nbar")
#expect(textView.selectedRanges == [NSRange(0..<3), NSRange(4..<7)] as [NSValue])
textView.selectedRanges = [NSRange(1..<1)] as [NSValue]
textView.insertionLocations = [5]
textView.commentOut(types: .block, fromLineHead: true)
XCTAssertEqual(textView.string, "<-foo->\n<-bar->")
XCTAssertEqual(textView.rangesForUserTextChange, [NSRange(3..<3), NSRange(11..<11)] as [NSValue])
XCTAssertTrue(textView.canUncomment(partly: false))
#expect(textView.string == "<-foo->\n<-bar->")
#expect(textView.rangesForUserTextChange == [NSRange(3..<3), NSRange(11..<11)] as [NSValue])
#expect(textView.canUncomment(partly: false))
textView.uncomment()
XCTAssertEqual(textView.string, "foo\nbar")
XCTAssertEqual(textView.rangesForUserTextChange, [NSRange(1..<1), NSRange(5..<5)] as [NSValue])
#expect(textView.string == "foo\nbar")
#expect(textView.rangesForUserTextChange == [NSRange(1..<1), NSRange(5..<5)] as [NSValue])
}
@MainActor func testIncompatibility() {
@MainActor @Test func checkIncompatibility() {
let textView = CommentingTextView()
@ -144,8 +145,8 @@ final class StringCommentingTests: XCTestCase {
// foo bar
"""
textView.selectedRange = textView.string.nsRange
XCTAssertTrue(textView.canUncomment(partly: false))
XCTAssertTrue(textView.canUncomment(partly: true))
#expect(textView.canUncomment(partly: false))
#expect(textView.canUncomment(partly: true))
textView.string = """
// foo
@ -153,8 +154,8 @@ final class StringCommentingTests: XCTestCase {
// foo bar
"""
textView.selectedRange = textView.string.nsRange
XCTAssertFalse(textView.canUncomment(partly: false))
XCTAssertTrue(textView.canUncomment(partly: true))
#expect(!textView.canUncomment(partly: false))
#expect(textView.canUncomment(partly: true))
}
}

View File

@ -24,28 +24,27 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class StringExtensionsTests: XCTestCase {
struct StringExtensionsTests {
/// Tests if the U+FEFF omitting bug on Swift 5 still exists.
///
/// - Bug: <https://bugs.swift.org/browse/SR-10896>
func testFEFF() {
@Test(.bug("https://bugs.swift.org/browse/SR-10896")) func feff() {
let bom = "\u{feff}"
// -> Some of these test cases must fail if the bug fixed.
XCTAssertEqual(bom.count, 1)
XCTAssertEqual(("\(bom)abc").count, 4)
XCTAssertEqual(NSString(string: bom).length, 0) // correct: 1
XCTAssertEqual(NSString(string: "\(bom)\(bom)").length, 1) // correct: 2
XCTAssertEqual(NSString(string: "\(bom)abc").length, 3) // correct: 4
XCTAssertEqual(NSString(string: "a\(bom)bc").length, 4)
#expect(bom.count == 1)
#expect(("\(bom)abc").count == 4)
#expect(NSString(string: bom).length == 0) // correct: 1
#expect(NSString(string: "\(bom)\(bom)").length == 1) // correct: 2
#expect(NSString(string: "\(bom)abc").length == 3) // correct: 4
#expect(NSString(string: "a\(bom)bc").length == 4)
let string = "\(bom)abc"
XCTAssertNotEqual(string.immutable, string) // -> This test must fail if the bug fixed.
#expect(string.immutable != string) // -> This test must fail if the bug fixed.
// Implicit NSString cast is fixed.
// -> However, still crashes when `string.immutable.enumerateSubstrings(in:)`
@ -54,334 +53,334 @@ final class StringExtensionsTests: XCTestCase {
}
func testCharacterEscape() {
@Test func escapeCharacter() {
let string = "a\\a\\\\aa"
XCTAssertFalse(string.isCharacterEscaped(at: 0))
XCTAssertTrue(string.isCharacterEscaped(at: 2))
XCTAssertFalse(string.isCharacterEscaped(at: 5))
#expect(!string.isCharacterEscaped(at: 0))
#expect(string.isCharacterEscaped(at: 2))
#expect(!string.isCharacterEscaped(at: 5))
}
func testUnescaping() {
@Test func unescape() {
XCTAssertEqual(#"\\"#.unescaped, "\\")
XCTAssertEqual(#"\'"#.unescaped, "\'")
XCTAssertEqual(#"\""#.unescaped, "\"")
XCTAssertEqual(#"a\n "#.unescaped, "a\n ")
XCTAssertEqual(#"a\\n "#.unescaped, "a\\n ")
XCTAssertEqual(#"a\\\n "#.unescaped, "a\\\n ")
XCTAssertEqual(#"a\\\\n"#.unescaped, "a\\\\n")
XCTAssertEqual(#"\\\\\t"#.unescaped, "\\\\\t")
XCTAssertEqual(#"\\foo\\\\\0bar\\"#.unescaped, "\\foo\\\\\u{0}bar\\")
XCTAssertEqual(#"\\\\\\\\foo"#.unescaped, "\\\\\\\\foo")
XCTAssertEqual(#"foo: \r\n1"#.unescaped, "foo: \r\n1")
#expect(#"\\"#.unescaped == "\\")
#expect(#"\'"#.unescaped == "\'")
#expect(#"\""#.unescaped == "\"")
#expect(#"a\n "#.unescaped == "a\n ")
#expect(#"a\\n "#.unescaped == "a\\n ")
#expect(#"a\\\n "#.unescaped == "a\\\n ")
#expect(#"a\\\\n"#.unescaped == "a\\\\n")
#expect(#"\\\\\t"#.unescaped == "\\\\\t")
#expect(#"\\foo\\\\\0bar\\"#.unescaped == "\\foo\\\\\u{0}bar\\")
#expect(#"\\\\\\\\foo"#.unescaped == "\\\\\\\\foo")
#expect(#"foo: \r\n1"#.unescaped == "foo: \r\n1")
}
func testComposedCharactersCount() {
@Test func countComposedCharacters() {
// make sure that `String.count` counts characters as I want
XCTAssertEqual("foo".count, 3)
XCTAssertEqual("\r\n".count, 1)
XCTAssertEqual("😀🇯🇵a".count, 3)
XCTAssertEqual("😀🏻".count, 1)
XCTAssertEqual("👍🏻".count, 1)
#expect("foo".count == 3)
#expect("\r\n".count == 1)
#expect("😀🇯🇵a".count == 3)
#expect("😀🏻".count == 1)
#expect("👍🏻".count == 1)
// single regional indicator
XCTAssertEqual("🇦 ".count, 2)
#expect("🇦 ".count == 2)
}
func testWordsCount() {
@Test func countWords() {
XCTAssertEqual("Clarus says moof!".numberOfWords, 3)
XCTAssertEqual("plain-text".numberOfWords, 2)
XCTAssertEqual("!".numberOfWords, 0)
XCTAssertEqual("".numberOfWords, 0)
#expect("Clarus says moof!".numberOfWords == 3)
#expect("plain-text".numberOfWords == 2)
#expect("!".numberOfWords == 0)
#expect("".numberOfWords == 0)
}
func testLinesCount() {
@Test func countLines() {
XCTAssertEqual("".numberOfLines, 0)
XCTAssertEqual("a".numberOfLines, 1)
XCTAssertEqual("\n".numberOfLines, 1)
XCTAssertEqual("\n\n".numberOfLines, 2)
XCTAssertEqual("\u{feff}".numberOfLines, 1)
XCTAssertEqual("ab\r\ncd".numberOfLines, 2)
#expect("".numberOfLines == 0)
#expect("a".numberOfLines == 1)
#expect("\n".numberOfLines == 1)
#expect("\n\n".numberOfLines == 2)
#expect("\u{feff}".numberOfLines == 1)
#expect("ab\r\ncd".numberOfLines == 2)
let testString = "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines, 3)
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0)), 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1)), 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2)), 1) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6)), 2) // "a\nb c\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<7)), 3) // "a\nb c\n\n"
#expect(testString.numberOfLines == 3)
#expect(testString.numberOfLines(in: NSRange(0..<0)) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1)) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2)) == 1) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6)) == 2) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7)) == 3) // "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true), 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true), 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true), 2) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6), includesLastBreak: true), 3) // "a\nb c\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<7), includesLastBreak: true), 4) // "a\nb c\n\n"
#expect(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true) == 0) // ""
#expect(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true) == 1) // "a"
#expect(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true) == 2) // "a\n"
#expect(testString.numberOfLines(in: NSRange(0..<6), includesLastBreak: true) == 3) // "a\nb c\n"
#expect(testString.numberOfLines(in: NSRange(0..<7), includesLastBreak: true) == 4) // "a\nb c\n\n"
XCTAssertEqual(testString.lineNumber(at: 0), 1)
XCTAssertEqual(testString.lineNumber(at: 1), 1)
XCTAssertEqual(testString.lineNumber(at: 2), 2)
XCTAssertEqual(testString.lineNumber(at: 5), 2)
XCTAssertEqual(testString.lineNumber(at: 6), 3)
XCTAssertEqual(testString.lineNumber(at: 7), 4)
#expect(testString.lineNumber(at: 0) == 1)
#expect(testString.lineNumber(at: 1) == 1)
#expect(testString.lineNumber(at: 2) == 2)
#expect(testString.lineNumber(at: 5) == 2)
#expect(testString.lineNumber(at: 6) == 3)
#expect(testString.lineNumber(at: 7) == 4)
let nsString = testString as NSString
XCTAssertEqual(nsString.lineNumber(at: 0), testString.lineNumber(at: 0))
XCTAssertEqual(nsString.lineNumber(at: 1), testString.lineNumber(at: 1))
XCTAssertEqual(nsString.lineNumber(at: 2), testString.lineNumber(at: 2))
XCTAssertEqual(nsString.lineNumber(at: 5), testString.lineNumber(at: 5))
XCTAssertEqual(nsString.lineNumber(at: 6), testString.lineNumber(at: 6))
XCTAssertEqual(nsString.lineNumber(at: 7), testString.lineNumber(at: 7))
#expect(nsString.lineNumber(at: 0) == testString.lineNumber(at: 0))
#expect(nsString.lineNumber(at: 1) == testString.lineNumber(at: 1))
#expect(nsString.lineNumber(at: 2) == testString.lineNumber(at: 2))
#expect(nsString.lineNumber(at: 5) == testString.lineNumber(at: 5))
#expect(nsString.lineNumber(at: 6) == testString.lineNumber(at: 6))
#expect(nsString.lineNumber(at: 7) == testString.lineNumber(at: 7))
XCTAssertEqual("\u{FEFF}".numberOfLines(in: NSRange(0..<1)), 1) // "\u{FEFF}"
XCTAssertEqual("\u{FEFF}\nb".numberOfLines(in: NSRange(0..<3)), 2) // "\u{FEFF}\nb"
XCTAssertEqual("a\u{FEFF}\nb".numberOfLines(in: NSRange(1..<4)), 2) // "\u{FEFF}\nb"
XCTAssertEqual("a\u{FEFF}\u{FEFF}\nb".numberOfLines(in: NSRange(1..<5)), 2) // "\u{FEFF}\nb"
#expect("\u{FEFF}".numberOfLines(in: NSRange(0..<1)) == 1) // "\u{FEFF}"
#expect("\u{FEFF}\nb".numberOfLines(in: NSRange(0..<3)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\nb".numberOfLines(in: NSRange(1..<4)) == 2) // "\u{FEFF}\nb"
#expect("a\u{FEFF}\u{FEFF}\nb".numberOfLines(in: NSRange(1..<5)) == 2) // "\u{FEFF}\nb"
XCTAssertEqual("a\u{FEFF}\nb".numberOfLines, 2)
XCTAssertEqual("\u{FEFF}\nb".numberOfLines, 2)
XCTAssertEqual("\u{FEFF}0000000000000000".numberOfLines, 1)
#expect("a\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}\nb".numberOfLines == 2)
#expect("\u{FEFF}0000000000000000".numberOfLines == 1)
let bomString = "\u{FEFF}\nb"
let range = bomString.startIndex..<bomString.index(bomString.startIndex, offsetBy: 2)
XCTAssertEqual(bomString.numberOfLines(in: [range, range]), 1) // "\u{FEFF}\n"
#expect(bomString.numberOfLines(in: [range, range]) == 1) // "\u{FEFF}\n"
}
func testColumnCount() {
@Test func countColumns() {
let string = "aaa \r\n🐱 "
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)), 3)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)), 4)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)), 0)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 6)), 1)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 7)), 2)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)) == 3)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)) == 4)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)) == 0)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 6)) == 1)
#expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 7)) == 2)
}
func testCharacterCountOptions() {
@Test func countCharactersWithOptions() {
var options = CharacterCountOptions()
let string = "aaa \t 🐱\n\r\n c"
XCTAssertEqual(string.count(options: options), string.count)
#expect(string.count(options: options) == string.count)
options.ignoresNewlines = true
XCTAssertEqual(string.count(options: options), 9)
#expect(string.count(options: options) == 9)
options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 5)
#expect(string.count(options: options) == 5)
options.ignoresNewlines = false
options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 7)
#expect(string.count(options: options) == 7)
// test .treatsConsecutiveWhitespaceAsSingle
options = .init()
options.treatsConsecutiveWhitespaceAsSingle = true
XCTAssertEqual(string.count(options: options), 7)
#expect(string.count(options: options) == 7)
options.ignoresNewlines = true
XCTAssertEqual(string.count(options: options), 7)
#expect(string.count(options: options) == 7)
options.treatsConsecutiveWhitespaceAsSingle = false
options.ignoresNewlines = true
options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 5)
#expect(string.count(options: options) == 5)
// test other units
options = .init()
options.unit = .unicodeScalar
XCTAssertEqual(string.count(options: options), 12)
#expect(string.count(options: options) == 12)
options.unit = .utf16
XCTAssertEqual(string.count(options: options), 13)
#expect(string.count(options: options) == 13)
// test normalization
let aUmlaut = ""
options = .init()
options.unit = .unicodeScalar
XCTAssertEqual(aUmlaut.count(options: options), 2)
#expect(aUmlaut.count(options: options) == 2)
options.normalizationForm = .nfc
XCTAssertEqual(aUmlaut.count(options: options), 1)
#expect(aUmlaut.count(options: options) == 1)
}
func testByteCountOptions() {
@Test func countBytes() {
let string = "abc犬牛"
var options = CharacterCountOptions(unit: .byte)
options.encoding = .utf8
XCTAssertEqual(string.count(options: options), 9)
#expect(string.count(options: options) == 9)
options.encoding = .shiftJIS
XCTAssertEqual(string.count(options: options), 7)
#expect(string.count(options: options) == 7)
options.encoding = .ascii
XCTAssertNil(string.count(options: options))
#expect(string.count(options: options) == nil)
options.encoding = .nonLossyASCII
XCTAssertEqual(string.count(options: options), 15)
#expect(string.count(options: options) == 15)
}
func testProgrammingCases() {
@Test func codingCases() {
XCTAssertEqual("AbcDefg Hij".snakecased, "abc_defg hij")
XCTAssertEqual("abcDefg Hij".snakecased, "abc_defg hij")
XCTAssertEqual("_abcDefg Hij".snakecased, "_abc_defg hij")
XCTAssertEqual("AA\u{0308}".snakecased, "a_a\u{0308}")
XCTAssertEqual("abÄb".snakecased, "ab_äb")
#expect("AbcDefg Hij".snakecased == "abc_defg hij")
#expect("abcDefg Hij".snakecased == "abc_defg hij")
#expect("_abcDefg Hij".snakecased == "_abc_defg hij")
#expect("AA\u{0308}".snakecased == "a_a\u{0308}")
#expect("abÄb".snakecased == "ab_äb")
XCTAssertEqual("abc_defg Hij".camelcased, "abcDefg hij")
XCTAssertEqual("AbcDefg Hij".camelcased, "abcDefg hij")
XCTAssertEqual("_abcDefg Hij".camelcased, "_abcDefg hij")
XCTAssertEqual("a_a\u{0308}".camelcased, "aA\u{0308}")
#expect("abc_defg Hij".camelcased == "abcDefg hij")
#expect("AbcDefg Hij".camelcased == "abcDefg hij")
#expect("_abcDefg Hij".camelcased == "_abcDefg hij")
#expect("a_a\u{0308}".camelcased == "aA\u{0308}")
XCTAssertEqual("abc_defg Hij".pascalcased, "AbcDefg Hij")
XCTAssertEqual("abcDefg Hij".pascalcased, "AbcDefg Hij")
XCTAssertEqual("_abcDefg Hij".pascalcased, "_abcDefg Hij")
XCTAssertEqual("a_a\u{0308}".pascalcased, "AA\u{0308}")
#expect("abc_defg Hij".pascalcased == "AbcDefg Hij")
#expect("abcDefg Hij".pascalcased == "AbcDefg Hij")
#expect("_abcDefg Hij".pascalcased == "_abcDefg Hij")
#expect("a_a\u{0308}".pascalcased == "AA\u{0308}")
}
func testJapaneseTransform() {
@Test func japaneseTransform() {
let testString = "犬 イヌ いぬ Dog 123 "
XCTAssertEqual(testString.fullwidthRoman(reverse: false), "犬 イヌ いぬ ")
XCTAssertEqual(testString.fullwidthRoman(reverse: true), "犬 イヌ いぬ Inu Dog 123 123")
#expect(testString.fullwidthRoman(reverse: false) == "犬 イヌ いぬ ")
#expect(testString.fullwidthRoman(reverse: true) == "犬 イヌ いぬ Inu Dog 123 123")
}
func testBeforeIndex() {
@Test func beforeIndex() {
XCTAssertEqual(("00" as NSString).index(before: 0), 0)
XCTAssertEqual(("00" as NSString).index(before: 1), 0)
XCTAssertEqual(("00" as NSString).index(before: 2), 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 1), 0)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 2), 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 5), 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 6), 5)
#expect(("00" as NSString).index(before: 0) == 0)
#expect(("00" as NSString).index(before: 1) == 0)
#expect(("00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 1) == 0)
#expect(("0🇦🇦00" as NSString).index(before: 2) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 5) == 1)
#expect(("0🇦🇦00" as NSString).index(before: 6) == 5)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 3), 1)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 2), 1)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 1), 0)
XCTAssertEqual(("0\n" as NSString).index(before: 1), 0)
XCTAssertEqual(("0\n" as NSString).index(before: 2), 1)
#expect(("0\r\n0" as NSString).index(before: 3) == 1)
#expect(("0\r\n0" as NSString).index(before: 2) == 1)
#expect(("0\r\n0" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 1) == 0)
#expect(("0\n" as NSString).index(before: 2) == 1)
}
func testAfterIndex() {
@Test func afterIndex() {
XCTAssertEqual(("00" as NSString).index(after: 0), 1)
XCTAssertEqual(("00" as NSString).index(after: 1), 2)
XCTAssertEqual(("00" as NSString).index(after: 2), 2)
XCTAssertEqual(("0🇦🇦0" as NSString).index(after: 0), 1)
XCTAssertEqual(("0🇦🇦0" as NSString).index(after: 1), 5)
#expect(("00" as NSString).index(after: 0) == 1)
#expect(("00" as NSString).index(after: 1) == 2)
#expect(("00" as NSString).index(after: 2) == 2)
#expect(("0🇦🇦0" as NSString).index(after: 0) == 1)
#expect(("0🇦🇦0" as NSString).index(after: 1) == 5)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 1), 3)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 2), 3)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 3), 4)
XCTAssertEqual(("0\r" as NSString).index(after: 1), 2)
XCTAssertEqual(("0\r" as NSString).index(after: 2), 2)
#expect(("0\r\n0" as NSString).index(after: 1) == 3)
#expect(("0\r\n0" as NSString).index(after: 2) == 3)
#expect(("0\r\n0" as NSString).index(after: 3) == 4)
#expect(("0\r" as NSString).index(after: 1) == 2)
#expect(("0\r" as NSString).index(after: 2) == 2)
// composed character does not care CRLF
XCTAssertEqual(("\r\n" as NSString).rangeOfComposedCharacterSequence(at: 1), NSRange(1..<2))
#expect(("\r\n" as NSString).rangeOfComposedCharacterSequence(at: 1) == NSRange(1..<2))
}
func testLineRange() {
@Test func lineRange() {
let string = "foo\n\rbar\n\r"
XCTAssertEqual(string.lineContentsRange(for: string.startIndex..<string.endIndex),
#expect(string.lineContentsRange(for: string.startIndex..<string.endIndex) ==
string.startIndex..<string.index(before: string.endIndex))
XCTAssertEqual(string.lineRange(at: string.index(after: string.startIndex)),
#expect(string.lineRange(at: string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 4))
XCTAssertEqual(string.lineContentsRange(for: string.startIndex..<string.index(after: string.startIndex)),
#expect(string.lineContentsRange(for: string.startIndex..<string.index(after: string.startIndex)) ==
string.startIndex..<string.index(string.startIndex, offsetBy: 3))
XCTAssertEqual((string as NSString).lineContentsRange(for: NSRange(..<1)), NSRange(..<3))
XCTAssertEqual((string as NSString).lineContentsRange(at: 5), NSRange(5..<8))
#expect((string as NSString).lineContentsRange(for: NSRange(..<1)) == NSRange(..<3))
#expect((string as NSString).lineContentsRange(at: 5) == NSRange(5..<8))
let emptyString = ""
let emptyRange = emptyString.startIndex..<emptyString.endIndex
XCTAssertEqual(emptyString.lineContentsRange(for: emptyRange), emptyRange)
#expect(emptyString.lineContentsRange(for: emptyRange) == emptyRange)
}
func testLineRanges() {
@Test func lineRanges() {
XCTAssertEqual("foo\nbar".lineContentsRanges(for: NSRange(1..<1)), [NSRange(1..<1)])
XCTAssertEqual("foo\nbar".lineContentsRanges(), [NSRange(0..<3), NSRange(4..<7)])
XCTAssertEqual("foo\nbar\n".lineContentsRanges(), [NSRange(0..<3), NSRange(4..<7)])
XCTAssertEqual("foo\r\nbar".lineContentsRanges(), [NSRange(0..<3), NSRange(5..<8)])
XCTAssertEqual("foo\r\r\rbar".lineContentsRanges().count, 4)
#expect("foo\nbar".lineContentsRanges(for: NSRange(1..<1)) == [NSRange(1..<1)])
#expect("foo\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\nbar\n".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
#expect("foo\r\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(5..<8)])
#expect("foo\r\r\rbar".lineContentsRanges().count == 4)
}
func testFirstLineEnding() {
@Test func firstLineEnding() {
XCTAssertEqual("foo\r\nbar".firstLineEnding, "\r\n")
#expect("foo\r\nbar".firstLineEnding == "\r\n")
}
func testRangeOfCharacter() {
@Test func rangeOfCharacter() {
let set = CharacterSet(charactersIn: "._")
let string = "abc.d🐕f_ghij" as NSString
XCTAssertEqual(string.substring(with: string.rangeOfCharacter(until: set, at: 0)), "abc")
XCTAssertEqual(string.substring(with: string.rangeOfCharacter(until: set, at: 4)), "d🐕f")
XCTAssertEqual(string.substring(with: string.rangeOfCharacter(until: set, at: string.length - 1)), "ghij")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 0)) == "abc")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: 4)) == "d🐕f")
#expect(string.substring(with: string.rangeOfCharacter(until: set, at: string.length - 1)) == "ghij")
}
func testComposedCharacterSequence() {
@Test func composedCharacterSequence() {
let blackDog = "🐕‍⬛" // 4
XCTAssertEqual(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1), 0)
#expect(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1) == 0)
let abcDog = "🐕⬛abc" // 4 1 1 1
XCTAssertEqual(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1), "🐕⬛a".utf16.count)
XCTAssertEqual(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1), "🐕‍⬛".utf16.count)
#expect(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕⬛a".utf16.count)
#expect(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == "🐕‍⬛".utf16.count)
let dogDog = "🐕‍⬛🐕" // 4 2
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1), 0)
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1), "🐕‍⬛".utf16.count)
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0), "🐕‍⬛🐕".utf16.count)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == 0)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕‍⬛".utf16.count)
#expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0) == "🐕‍⬛🐕".utf16.count)
let string = "🐕🏴‍☠️🇯🇵🧑‍💻" // 2 5 4 5
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3), 0)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2), 0)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1), "🐕".utf16.count)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0), "🐕🏴‍☠️".utf16.count)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2) == 0)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1) == "🐕".utf16.count)
#expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0) == "🐕🏴‍☠️".utf16.count)
let abc = "abc"
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2), 0)
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1), 0)
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0), 1)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1) == 0)
#expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0) == 1)
}
func testUnicodeNormalization() {
@Test func normalizeUnicode() {
XCTAssertEqual("\t 神 ㍑ C".precomposedStringWithCompatibilityMappingWithCasefold, "é \t 神 リットル abc")
XCTAssertEqual("\u{1f71} \u{03b1}\u{0301}".precomposedStringWithHFSPlusMapping, "\u{1f71} \u{03ac}")
XCTAssertEqual("\u{1f71}".precomposedStringWithHFSPlusMapping, "\u{1f71}") // test single char
XCTAssertEqual("\u{1f71}".decomposedStringWithHFSPlusMapping, "\u{03b1}\u{0301}")
#expect("\t 神 ㍑ C".precomposedStringWithCompatibilityMappingWithCasefold == "é \t 神 リットル abc")
#expect("\u{1f71} \u{03b1}\u{0301}".precomposedStringWithHFSPlusMapping == "\u{1f71} \u{03ac}")
#expect("\u{1f71}".precomposedStringWithHFSPlusMapping == "\u{1f71}") // test single char
#expect("\u{1f71}".decomposedStringWithHFSPlusMapping == "\u{03b1}\u{0301}")
}
func testWhitespaceTrimming() throws {
@Test func trimWhitespace() throws {
let string = """
@ -399,7 +398,7 @@ final class StringExtensionsTests: XCTestCase {
white space ->
abc
"""
XCTAssertEqual(trimmed, expectedTrimmed)
#expect(trimmed == expectedTrimmed)
let trimmedIgnoringEmptyLines = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoresEmptyLines: true))
let expectedTrimmedIgnoringEmptyLines = """
@ -409,36 +408,36 @@ final class StringExtensionsTests: XCTestCase {
white space ->
abc
"""
XCTAssertEqual(trimmedIgnoringEmptyLines, expectedTrimmedIgnoringEmptyLines)
#expect(trimmedIgnoringEmptyLines == expectedTrimmedIgnoringEmptyLines)
}
func testAbbreviatedMatch() throws {
@Test func abbreviatedMatch() throws {
let string = "The fox jumps over the lazy dogcow."
XCTAssertNil(string.abbreviatedMatch(with: "quick"))
#expect(string.abbreviatedMatch(with: "quick") == nil)
let dogcow = try XCTUnwrap(string.abbreviatedMatch(with: "dogcow"))
XCTAssertEqual(dogcow.score, 6)
XCTAssertEqual(dogcow.ranges.count, 6)
XCTAssertEqual(dogcow.remaining, "")
let dogcow = try #require(string.abbreviatedMatch(with: "dogcow"))
#expect(dogcow.score == 6)
#expect(dogcow.ranges.count == 6)
#expect(dogcow.remaining.isEmpty)
let ow = try XCTUnwrap(string.abbreviatedMatch(with: "ow"))
XCTAssertEqual(ow.score, 29)
XCTAssertEqual(ow.ranges.count, 2)
XCTAssertEqual(ow.remaining, "")
let ow = try #require(string.abbreviatedMatch(with: "ow"))
#expect(ow.score == 29)
#expect(ow.ranges.count == 2)
#expect(ow.remaining.isEmpty)
let lazyTanuki = try XCTUnwrap(string.abbreviatedMatch(with: "lazy tanuki"))
XCTAssertEqual(lazyTanuki.score, 5)
XCTAssertEqual(lazyTanuki.ranges.count, 5)
XCTAssertEqual(lazyTanuki.remaining, "tanuki")
let lazyTanuki = try #require(string.abbreviatedMatch(with: "lazy tanuki"))
#expect(lazyTanuki.score == 5)
#expect(lazyTanuki.ranges.count == 5)
#expect(lazyTanuki.remaining == "tanuki")
XCTAssertNil(string.abbreviatedMatchedRanges(with: "lazy tanuki"))
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count, 5)
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki") == nil)
#expect(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count == 5)
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy w")?.count, 6)
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy w", incomplete: true)?.count, 6)
#expect(string.abbreviatedMatchedRanges(with: "lazy w")?.count == 6)
#expect(string.abbreviatedMatchedRanges(with: "lazy w", incomplete: true)?.count == 6)
}
}
@ -449,7 +448,7 @@ private extension String {
func trim(ranges: [NSRange]) throws -> String {
try ranges.reversed()
.map { try XCTUnwrap(Range($0, in: self)) }
.map { try #require(Range($0, in: self)) }
.reduce(self) { $0.replacingCharacters(in: $1, with: "") }
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2015-2023 1024jp
// © 2015-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,71 +24,72 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class StringIndentationTests: XCTestCase {
struct StringIndentationTests {
// MARK: Indentation Style Detection Tests
func testIndentStyleDetection() {
@Test func detectIndentStyle() {
let string = "\t\tfoo\tbar"
XCTAssertNil(string.detectedIndentStyle)
#expect(string.detectedIndentStyle == nil)
}
// MARK: Indentation Style Standardization Tests
func testIndentStyleStandardizationToTab() {
@Test func standardizeIndentStyleToTab() {
let string = " foo bar\n "
// spaces to tab
XCTAssertEqual(string.standardizingIndent(to: .tab, tabWidth: 2), "\t\t foo bar\n\t")
XCTAssertEqual(string.standardizingIndent(to: .space, tabWidth: 2), string)
#expect(string.standardizingIndent(to: .tab, tabWidth: 2) == "\t\t foo bar\n\t")
#expect(string.standardizingIndent(to: .space, tabWidth: 2) == string)
}
func testIndentStyleStandardizationToSpace() {
@Test func standardizeIndentStyleToSpace() {
let string = "\t\tfoo\tbar"
XCTAssertEqual(string.standardizingIndent(to: .space, tabWidth: 2), " foo\tbar")
XCTAssertEqual(string.standardizingIndent(to: .tab, tabWidth: 2), string)
#expect(string.standardizingIndent(to: .space, tabWidth: 2) == " foo\tbar")
#expect(string.standardizingIndent(to: .tab, tabWidth: 2) == string)
}
// MARK: Other Tests
func testIndentLevelDetection() {
@Test func detectIndentLevel() {
XCTAssertEqual(" foo".indentLevel(at: 0, tabWidth: 4), 1)
XCTAssertEqual(" foo".indentLevel(at: 4, tabWidth: 2), 2)
XCTAssertEqual("\tfoo".indentLevel(at: 4, tabWidth: 2), 1)
#expect(" foo".indentLevel(at: 0, tabWidth: 4) == 1)
#expect(" foo".indentLevel(at: 4, tabWidth: 2) == 2)
#expect("\tfoo".indentLevel(at: 4, tabWidth: 2) == 1)
// tab-space mix
XCTAssertEqual(" \t foo".indentLevel(at: 4, tabWidth: 2), 2)
XCTAssertEqual(" \t foo".indentLevel(at: 4, tabWidth: 2), 3)
#expect(" \t foo".indentLevel(at: 4, tabWidth: 2) == 2)
#expect(" \t foo".indentLevel(at: 4, tabWidth: 2) == 3)
// multiline
XCTAssertEqual(" foo\n bar".indentLevel(at: 10, tabWidth: 2), 1)
#expect(" foo\n bar".indentLevel(at: 10, tabWidth: 2) == 1)
}
func testSoftTabDeletion() {
@Test func deleteSoftTab() {
let string = " foo\n bar "
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(0..<0), tabWidth: 2))
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(4..<5), tabWidth: 2))
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(6..<6), tabWidth: 2))
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(5..<5), tabWidth: 2), NSRange(4..<5))
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(4..<4), tabWidth: 2), NSRange(2..<4))
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(10..<10), tabWidth: 2))
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(11..<11), tabWidth: 2), NSRange(9..<11))
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(16..<16), tabWidth: 2))
#expect(string.rangeForSoftTabDeletion(in: NSRange(0..<0), tabWidth: 2) == nil)
#expect(string.rangeForSoftTabDeletion(in: NSRange(4..<5), tabWidth: 2) == nil)
#expect(string.rangeForSoftTabDeletion(in: NSRange(6..<6), tabWidth: 2) == nil)
#expect(string.rangeForSoftTabDeletion(in: NSRange(5..<5), tabWidth: 2) == NSRange(4..<5))
#expect(string.rangeForSoftTabDeletion(in: NSRange(4..<4), tabWidth: 2) == NSRange(2..<4))
#expect(string.rangeForSoftTabDeletion(in: NSRange(10..<10), tabWidth: 2) == nil)
#expect(string.rangeForSoftTabDeletion(in: NSRange(11..<11), tabWidth: 2) == NSRange(9..<11))
#expect(string.rangeForSoftTabDeletion(in: NSRange(16..<16), tabWidth: 2) == nil)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2020-2022 1024jp
// © 2020-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,12 +23,13 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class StringLineProcessingTests: XCTestCase {
struct StringLineProcessingTests {
func testMoveLineUp() {
@Test func moveLineUp() throws {
let string = """
aa
@ -37,29 +38,28 @@ final class StringLineProcessingTests: XCTestCase {
d
eee
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.moveLineUp(in: [NSRange(4, 1)])
XCTAssertEqual(info?.strings, ["bbbb\naa\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 8)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1)])
info = try #require(string.moveLineUp(in: [NSRange(4, 1)]))
#expect(info.strings == ["bbbb\naa\n"])
#expect(info.ranges == [NSRange(0, 8)])
#expect(info.selectedRanges == [NSRange(1, 1)])
info = string.moveLineUp(in: [NSRange(4, 1), NSRange(6, 0)])
XCTAssertEqual(info?.strings, ["bbbb\naa\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 8)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1), NSRange(3, 0)])
info = try #require(string.moveLineUp(in: [NSRange(4, 1), NSRange(6, 0)]))
#expect(info.strings == ["bbbb\naa\n"])
#expect(info.ranges == [NSRange(0, 8)])
#expect(info.selectedRanges == [NSRange(1, 1), NSRange(3, 0)])
info = string.moveLineUp(in: [NSRange(4, 1), NSRange(9, 0), NSRange(15, 1)])
XCTAssertEqual(info?.strings, ["bbbb\nccc\naa\neee\nd"])
XCTAssertEqual(info?.ranges, [NSRange(0, 17)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1), NSRange(6, 0), NSRange(13, 1)])
info = try #require(string.moveLineUp(in: [NSRange(4, 1), NSRange(9, 0), NSRange(15, 1)]))
#expect(info.strings == ["bbbb\nccc\naa\neee\nd"])
#expect(info.ranges == [NSRange(0, 17)])
#expect(info.selectedRanges == [NSRange(1, 1), NSRange(6, 0), NSRange(13, 1)])
info = string.moveLineUp(in: [NSRange(2, 1)])
XCTAssertNil(info)
#expect(string.moveLineUp(in: [NSRange(2, 1)]) == nil)
}
func testMoveLineDown() {
@Test func moveLineDown() throws {
let string = """
aa
@ -68,77 +68,74 @@ final class StringLineProcessingTests: XCTestCase {
d
eee
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.moveLineDown(in: [NSRange(4, 1)])
XCTAssertEqual(info?.strings, ["aa\nccc\nbbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 12)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1)])
info = try #require(string.moveLineDown(in: [NSRange(4, 1)]))
#expect(info.strings == ["aa\nccc\nbbbb\n"])
#expect(info.ranges == [NSRange(0, 12)])
#expect(info.selectedRanges == [NSRange(8, 1)])
info = string.moveLineDown(in: [NSRange(4, 1), NSRange(6, 0)])
XCTAssertEqual(info?.strings, ["aa\nccc\nbbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 12)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1), NSRange(10, 0)])
info = try #require(string.moveLineDown(in: [NSRange(4, 1), NSRange(6, 0)]))
#expect(info.strings == ["aa\nccc\nbbbb\n"])
#expect(info.ranges == [NSRange(0, 12)])
#expect(info.selectedRanges == [NSRange(8, 1), NSRange(10, 0)])
info = string.moveLineDown(in: [NSRange(4, 1), NSRange(9, 0), NSRange(13, 1)])
XCTAssertEqual(info?.strings, ["aa\neee\nbbbb\nccc\nd"])
XCTAssertEqual(info?.ranges, [NSRange(0, 17)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1), NSRange(13, 0), NSRange(17, 1)])
info = try #require(string.moveLineDown(in: [NSRange(4, 1), NSRange(9, 0), NSRange(13, 1)]))
#expect(info.strings == ["aa\neee\nbbbb\nccc\nd"])
#expect(info.ranges == [NSRange(0, 17)])
#expect(info.selectedRanges == [NSRange(8, 1), NSRange(13, 0), NSRange(17, 1)])
info = string.moveLineDown(in: [NSRange(14, 1)])
XCTAssertNil(info)
#expect(string.moveLineDown(in: [NSRange(14, 1)]) == nil)
}
func testSortLinesAscending() {
@Test func sortLinesAscending() throws {
let string = """
ccc
aa
bbbb
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.sortLinesAscending(in: NSRange(4, 1))
XCTAssertNil(info)
#expect(string.sortLinesAscending(in: NSRange(4, 1)) == nil)
info = string.sortLinesAscending(in: string.nsRange)
XCTAssertEqual(info?.strings, ["aa\nbbbb\nccc"])
XCTAssertEqual(info?.ranges, [NSRange(0, 11)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 11)])
info = try #require(string.sortLinesAscending(in: string.nsRange))
#expect(info.strings == ["aa\nbbbb\nccc"])
#expect(info.ranges == [NSRange(0, 11)])
#expect(info.selectedRanges == [NSRange(0, 11)])
info = string.sortLinesAscending(in: NSRange(2, 4))
XCTAssertEqual(info?.strings, ["aa\nccc"])
XCTAssertEqual(info?.ranges, [NSRange(0, 6)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 6)])
info = try #require(string.sortLinesAscending(in: NSRange(2, 4)))
#expect(info.strings == ["aa\nccc"])
#expect(info.ranges == [NSRange(0, 6)])
#expect(info.selectedRanges == [NSRange(0, 6)])
}
func testReverseLines() {
@Test func reverseLines() throws {
let string = """
aa
bbbb
ccc
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.reverseLines(in: NSRange(4, 1))
XCTAssertNil(info)
#expect(string.reverseLines(in: NSRange(4, 1)) == nil)
info = string.reverseLines(in: string.nsRange)
XCTAssertEqual(info?.strings, ["ccc\nbbbb\naa"])
XCTAssertEqual(info?.ranges, [NSRange(0, 11)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 11)])
info = try #require(string.reverseLines(in: string.nsRange))
#expect(info.strings == ["ccc\nbbbb\naa"])
#expect(info.ranges == [NSRange(0, 11)])
#expect(info.selectedRanges == [NSRange(0, 11)])
info = string.reverseLines(in: NSRange(2, 4))
XCTAssertEqual(info?.strings, ["bbbb\naa"])
XCTAssertEqual(info?.ranges, [NSRange(0, 7)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 7)])
info = try #require(string.reverseLines(in: NSRange(2, 4)))
#expect(info.strings == ["bbbb\naa"])
#expect(info.ranges == [NSRange(0, 7)])
#expect(info.selectedRanges == [NSRange(0, 7)])
}
func testDeleteDuplicateLine() {
@Test func deleteDuplicateLine() throws {
let string = """
aa
@ -147,76 +144,75 @@ final class StringLineProcessingTests: XCTestCase {
ccc
bbbb
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.deleteDuplicateLine(in: [NSRange(4, 1)])
XCTAssertNil(info)
#expect(string.deleteDuplicateLine(in: [NSRange(4, 1)]) == nil)
info = string.deleteDuplicateLine(in: [string.nsRange])
XCTAssertEqual(info?.strings, ["", ""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4), NSRange(16, 4)])
XCTAssertNil(info?.selectedRanges)
info = try #require(string.deleteDuplicateLine(in: [string.nsRange]))
#expect(info.strings == ["", ""])
#expect(info.ranges == [NSRange(12, 4), NSRange(16, 4)])
#expect(info.selectedRanges == nil)
info = string.deleteDuplicateLine(in: [NSRange(10, 4)])
XCTAssertEqual(info?.strings, [""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4)])
XCTAssertNil(info?.selectedRanges)
info = try #require(string.deleteDuplicateLine(in: [NSRange(10, 4)]))
#expect(info.strings == [""])
#expect(info.ranges == [NSRange(12, 4)])
#expect(info.selectedRanges == nil)
info = string.deleteDuplicateLine(in: [NSRange(9, 1), NSRange(11, 0), NSRange(13, 2)])
XCTAssertEqual(info?.strings, [""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4)])
XCTAssertNil(info?.selectedRanges)
info = try #require(string.deleteDuplicateLine(in: [NSRange(9, 1), NSRange(11, 0), NSRange(13, 2)]))
#expect(info.strings == [""])
#expect(info.ranges == [NSRange(12, 4)])
#expect(info.selectedRanges == nil)
}
func testDuplicateLine() {
@Test func duplicateLine() throws {
let string = """
aa
bbbb
ccc
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.duplicateLine(in: [NSRange(4, 1)], lineEnding: "\n")
XCTAssertEqual(info?.strings, ["bbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(9, 1)])
info = try #require(string.duplicateLine(in: [NSRange(4, 1)], lineEnding: "\n"))
#expect(info.strings == ["bbbb\n"])
#expect(info.ranges == [NSRange(3, 0)])
#expect(info.selectedRanges == [NSRange(9, 1)])
info = string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 4)], lineEnding: "\n")
XCTAssertEqual(info?.strings, ["bbbb\nccc\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(13, 1), NSRange(15, 4)])
info = try #require(string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 4)], lineEnding: "\n"))
#expect(info.strings == ["bbbb\nccc\n"])
#expect(info.ranges == [NSRange(3, 0)])
#expect(info.selectedRanges == [NSRange(13, 1), NSRange(15, 4)])
info = string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)], lineEnding: "\n")
XCTAssertEqual(info?.strings, ["bbbb\n", "ccc\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0), NSRange(8, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(9, 1), NSRange(11, 1), NSRange(19, 0)])
info = try #require(string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)], lineEnding: "\n"))
#expect(info.strings == ["bbbb\n", "ccc\n"])
#expect(info.ranges == [NSRange(3, 0), NSRange(8, 0)])
#expect(info.selectedRanges == [NSRange(9, 1), NSRange(11, 1), NSRange(19, 0)])
}
func testDeleteLine() {
@Test func deleteLine() throws {
let string = """
aa
bbbb
ccc
"""
var info: String.EditingInfo?
var info: String.EditingInfo
info = string.deleteLine(in: [NSRange(4, 1)])
XCTAssertEqual(info?.strings, [""])
XCTAssertEqual(info?.ranges, [NSRange(3, 5)])
XCTAssertEqual(info?.selectedRanges, [NSRange(3, 0)])
info = try #require(string.deleteLine(in: [NSRange(4, 1)]))
#expect(info.strings == [""])
#expect(info.ranges == [NSRange(3, 5)])
#expect(info.selectedRanges == [NSRange(3, 0)])
info = string.deleteLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)])
XCTAssertEqual(info?.strings, ["", ""])
XCTAssertEqual(info?.ranges, [NSRange(3, 5), NSRange(8, 3)])
XCTAssertEqual(info?.selectedRanges, [NSRange(3, 0)])
info = try #require(string.deleteLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)]))
#expect(info.strings == ["", ""])
#expect(info.ranges == [NSRange(3, 5), NSRange(8, 3)])
#expect(info.selectedRanges == [NSRange(3, 0)])
}
func testJoinLinesIn() {
@Test func joinLinesIn() {
let string = """
aa
@ -226,13 +222,13 @@ final class StringLineProcessingTests: XCTestCase {
"""
let info = string.joinLines(in: [NSRange(1, 6), NSRange(10, 1)])
XCTAssertEqual(info.strings, ["a bb", "c"])
XCTAssertEqual(info.ranges, [NSRange(1, 6), NSRange(10, 1)])
XCTAssertEqual(info.selectedRanges, [NSRange(1, 4), NSRange(8, 1)])
#expect(info.strings == ["a bb", "c"])
#expect(info.ranges == [NSRange(1, 6), NSRange(10, 1)])
#expect(info.selectedRanges == [NSRange(1, 4), NSRange(8, 1)])
}
func testJoinLinesAfter() {
@Test func joinLinesAfter() {
let string = """
aa
@ -242,9 +238,9 @@ final class StringLineProcessingTests: XCTestCase {
"""
let info = string.joinLines(after: [NSRange(1, 0), NSRange(10, 0), NSRange(14, 0)])
XCTAssertEqual(info.strings, [" ", " "])
XCTAssertEqual(info.ranges, [NSRange(2, 3), NSRange(13, 1)])
XCTAssertNil(info.selectedRanges)
#expect(info.strings == [" ", " "])
#expect(info.ranges == [NSRange(2, 3), NSRange(13, 1)])
#expect(info.selectedRanges == nil)
}
}

View File

@ -24,12 +24,13 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
import Combine
import Yams
@testable import CotEditor
final class SyntaxTests: XCTestCase {
final class SyntaxTests {
private let syntaxDirectoryName = "Syntaxes"
@ -41,12 +42,10 @@ final class SyntaxTests: XCTestCase {
override func setUpWithError() throws {
try super.setUpWithError()
init() throws {
let bundle = Bundle(for: type(of: self))
let urls = try XCTUnwrap(bundle.urls(forResourcesWithExtension: "yml", subdirectory: self.syntaxDirectoryName))
let urls = try #require(bundle.urls(forResourcesWithExtension: "yml", subdirectory: self.syntaxDirectoryName))
// load syntaxes
let decoder = YAMLDecoder()
@ -56,129 +55,128 @@ final class SyntaxTests: XCTestCase {
dict[name] = try decoder.decode(Syntax.self, from: data)
}
self.htmlSyntax = try XCTUnwrap(self.syntaxes["HTML"])
XCTAssertNotNil(self.htmlSyntax)
self.htmlSyntax = try #require(self.syntaxes["HTML"])
// load test file
let sourceURL = try XCTUnwrap(bundle.url(forResource: "sample", withExtension: "html"))
let sourceURL = try #require(bundle.url(forResource: "sample", withExtension: "html"))
self.htmlSource = try String(contentsOf: sourceURL)
XCTAssertNotNil(self.htmlSource)
}
func testAllSyntaxes() {
@Test func loadHTML() {
#expect(self.htmlSyntax != nil)
#expect(self.htmlSource != nil)
}
@Test func allSyntaxes() {
for (name, syntax) in self.syntaxes {
let model = SyntaxObject(value: syntax)
let errors = model.validate()
XCTAssert(errors.isEmpty)
#expect(errors.isEmpty)
for error in errors {
XCTFail("\(name): \(error)")
Issue.record("\(name): \(error)")
}
}
}
func testSanitization() {
@Test func sanitize() {
for (name, syntax) in self.syntaxes {
let sanitized = syntax.sanitized
XCTAssertEqual(syntax.kind, sanitized.kind)
#expect(syntax.kind == sanitized.kind)
for type in SyntaxType.allCases {
let keyPath = Syntax.highlightKeyPath(for: type)
XCTAssertEqual(syntax[keyPath: keyPath], sanitized[keyPath: keyPath],
#expect(syntax[keyPath: keyPath] == sanitized[keyPath: keyPath],
".\(type.rawValue) of “\(name)” is not sanitized in the latest manner")
}
XCTAssertEqual(syntax.outlines, sanitized.outlines,
#expect(syntax.outlines == sanitized.outlines,
".outlines of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.completions, sanitized.completions,
#expect(syntax.completions == sanitized.completions,
".completions of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.commentDelimiters, sanitized.commentDelimiters,
#expect(syntax.commentDelimiters == sanitized.commentDelimiters,
".commentDelimiters of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.extensions, sanitized.extensions,
#expect(syntax.extensions == sanitized.extensions,
".extensions of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.filenames, sanitized.filenames,
#expect(syntax.filenames == sanitized.filenames,
".filenames of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.interpreters, sanitized.interpreters,
#expect(syntax.interpreters == sanitized.interpreters,
".interpreters of “\(name)” is not sanitized in the latest manner")
XCTAssertEqual(syntax.metadata, sanitized.metadata,
#expect(syntax.metadata == sanitized.metadata,
".metadata of “\(name)” is not sanitized in the latest manner")
}
}
func testEquality() {
XCTAssertEqual(self.htmlSyntax, self.htmlSyntax)
}
func testNoneSyntax() {
@Test func noneSyntax() {
let syntax = Syntax.none
XCTAssertEqual(syntax.kind, .code)
XCTAssert(syntax.highlightParser.isEmpty)
XCTAssertNil(syntax.commentDelimiters.inline)
XCTAssertNil(syntax.commentDelimiters.block)
#expect(syntax.kind == .code)
#expect(syntax.highlightParser.isEmpty)
#expect(syntax.commentDelimiters.inline == nil)
#expect(syntax.commentDelimiters.block == nil)
}
func testXMLSyntax() throws {
@Test func xmlSyntax() throws {
let syntax = try XCTUnwrap(self.htmlSyntax)
let syntax = try #require(self.htmlSyntax)
XCTAssertFalse(syntax.highlightParser.isEmpty)
XCTAssertNil(syntax.commentDelimiters.inline)
XCTAssertEqual(syntax.commentDelimiters.block, Pair("<!--", "-->"))
#expect(!syntax.highlightParser.isEmpty)
#expect(syntax.commentDelimiters.inline == nil)
#expect(syntax.commentDelimiters.block == Pair("<!--", "-->"))
}
func testOutlineParse() throws {
@Test func parseOutline() async throws {
let syntax = try XCTUnwrap(self.htmlSyntax)
let source = try XCTUnwrap(self.htmlSource)
let syntax = try #require(self.htmlSyntax)
let source = try #require(self.htmlSource)
let textStorage = NSTextStorage(string: source)
let parser = SyntaxParser(textStorage: textStorage, syntax: syntax, name: "HTML")
// test outline parsing with publisher
let outlineParseExpectation = self.expectation(description: "didParseOutline")
try await confirmation("didParseOutline") { confirm in
self.outlineParseCancellable = parser.$outlineItems
.compactMap { $0 } // ignore the initial invocation
.receive(on: RunLoop.main)
.sink { outlineItems in
outlineParseExpectation.fulfill()
confirm()
XCTAssertEqual(outlineItems.count, 3)
#expect(outlineItems.count == 3)
XCTAssertEqual(parser.outlineItems, outlineItems)
#expect(parser.outlineItems == outlineItems)
let item = outlineItems[1]
XCTAssertEqual(item.title, " h2: 🐕🐄")
XCTAssertEqual(item.range.location, 354)
XCTAssertEqual(item.range.length, 13)
XCTAssertTrue(item.style.isEmpty)
#expect(item.title == " h2: 🐕🐄")
#expect(item.range.location == 354)
#expect(item.range.length == 13)
#expect(item.style.isEmpty)
}
parser.invalidateOutline()
self.waitForExpectations(timeout: 1)
try await Task.sleep(for: .seconds(0.5))
}
}
func testViewModelHighlightEquality() {
@Test func viewModelHighlightEquality() {
let termA = SyntaxObject.Highlight(begin: "abc", end: "def")
let termB = SyntaxObject.Highlight(begin: "abc", end: "def")
let termC = SyntaxObject.Highlight(begin: "abc")
XCTAssertEqual(termA, termB)
XCTAssertNotEqual(termA, termC)
XCTAssertNotEqual(termA.id, termB.id)
#expect(termA == termB)
#expect(termA != termC)
#expect(termA.id != termB.id)
}
}

View File

@ -8,7 +8,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2020-2023 1024jp
// © 2020-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -23,17 +23,18 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class TextClippingTests: XCTestCase {
actor TextClippingTests {
func testReadingTextClippingFile() throws {
@Test func readTextClippingFile() throws {
let bundle = Bundle(for: type(of: self))
let url = try XCTUnwrap(bundle.url(forResource: "moof", withExtension: "textClipping"))
let url = try #require(bundle.url(forResource: "moof", withExtension: "textClipping"))
let textClipping = try TextClipping(contentsOf: url)
XCTAssertEqual(textClipping.string, "🐕moof🐄")
#expect(textClipping.string == "🐕moof🐄")
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2017-2023 1024jp
// © 2017-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,45 +24,32 @@
// limitations under the License.
//
import XCTest
import AppKit
import Testing
@testable import CotEditor
final class TextFindTests: XCTestCase {
struct TextFindTests {
func testTextFinderActions() {
@Test func finderActions() {
XCTAssertEqual(TextFinder.Action.showFindInterface.rawValue,
NSTextFinder.Action.showFindInterface.rawValue)
XCTAssertEqual(TextFinder.Action.nextMatch.rawValue,
NSTextFinder.Action.nextMatch.rawValue)
XCTAssertEqual(TextFinder.Action.previousMatch.rawValue,
NSTextFinder.Action.previousMatch.rawValue)
XCTAssertEqual(TextFinder.Action.replaceAll.rawValue,
NSTextFinder.Action.replaceAll.rawValue)
XCTAssertEqual(TextFinder.Action.replace.rawValue,
NSTextFinder.Action.replace.rawValue)
XCTAssertEqual(TextFinder.Action.replaceAndFind.rawValue,
NSTextFinder.Action.replaceAndFind.rawValue)
XCTAssertEqual(TextFinder.Action.setSearchString.rawValue,
NSTextFinder.Action.setSearchString.rawValue)
XCTAssertEqual(TextFinder.Action.replaceAllInSelection.rawValue,
NSTextFinder.Action.replaceAllInSelection.rawValue)
XCTAssertEqual(TextFinder.Action.selectAll.rawValue,
NSTextFinder.Action.selectAll.rawValue)
XCTAssertEqual(TextFinder.Action.selectAllInSelection.rawValue,
NSTextFinder.Action.selectAllInSelection.rawValue)
XCTAssertEqual(TextFinder.Action.hideFindInterface.rawValue,
NSTextFinder.Action.hideFindInterface.rawValue)
XCTAssertEqual(TextFinder.Action.showReplaceInterface.rawValue,
NSTextFinder.Action.showReplaceInterface.rawValue)
XCTAssertEqual(TextFinder.Action.showReplaceInterface.rawValue,
NSTextFinder.Action.showReplaceInterface.rawValue)
XCTAssertEqual(TextFinder.Action.hideReplaceInterface.rawValue,
NSTextFinder.Action.hideReplaceInterface.rawValue)
#expect(TextFinder.Action.showFindInterface.rawValue == NSTextFinder.Action.showFindInterface.rawValue)
#expect(TextFinder.Action.nextMatch.rawValue == NSTextFinder.Action.nextMatch.rawValue)
#expect(TextFinder.Action.previousMatch.rawValue == NSTextFinder.Action.previousMatch.rawValue)
#expect(TextFinder.Action.replaceAll.rawValue == NSTextFinder.Action.replaceAll.rawValue)
#expect(TextFinder.Action.replace.rawValue == NSTextFinder.Action.replace.rawValue)
#expect(TextFinder.Action.replaceAndFind.rawValue == NSTextFinder.Action.replaceAndFind.rawValue)
#expect(TextFinder.Action.setSearchString.rawValue == NSTextFinder.Action.setSearchString.rawValue)
#expect(TextFinder.Action.replaceAllInSelection.rawValue == NSTextFinder.Action.replaceAllInSelection.rawValue)
#expect(TextFinder.Action.selectAll.rawValue == NSTextFinder.Action.selectAll.rawValue)
#expect(TextFinder.Action.selectAllInSelection.rawValue == NSTextFinder.Action.selectAllInSelection.rawValue)
#expect(TextFinder.Action.hideFindInterface.rawValue == NSTextFinder.Action.hideFindInterface.rawValue)
#expect(TextFinder.Action.showReplaceInterface.rawValue == NSTextFinder.Action.showReplaceInterface.rawValue)
#expect(TextFinder.Action.showReplaceInterface.rawValue == NSTextFinder.Action.showReplaceInterface.rawValue)
#expect(TextFinder.Action.hideReplaceInterface.rawValue == NSTextFinder.Action.hideReplaceInterface.rawValue)
}
func testCaptureGroupCount() throws {
@Test func countCaptureGroup() throws {
var mode: TextFind.Mode
var textFind: TextFind
@ -70,18 +57,18 @@ final class TextFindTests: XCTestCase {
mode = .regularExpression(options: [], unescapesReplacement: false)
textFind = try TextFind(for: "", findString: "a", mode: mode)
XCTAssertEqual(textFind.numberOfCaptureGroups, 0)
#expect(textFind.numberOfCaptureGroups == 0)
textFind = try TextFind(for: "", findString: "(?!=a)(b)(c)(?=d)", mode: mode)
XCTAssertEqual(textFind.numberOfCaptureGroups, 2)
#expect(textFind.numberOfCaptureGroups == 2)
mode = .textual(options: [], fullWord: false)
textFind = try TextFind(for: "", findString: "(?!=a)(b)(c)(?=d)", mode: mode)
XCTAssertEqual(textFind.numberOfCaptureGroups, 0)
#expect(textFind.numberOfCaptureGroups == 0)
}
func testSingleFind() throws {
@Test func singleFind() throws {
let text = "abcdefg abcdefg ABCDEFG"
let findString = "abc"
@ -93,40 +80,40 @@ final class TextFindTests: XCTestCase {
textFind = try TextFind(for: text, findString: findString, mode: .textual(options: [], fullWord: false))
matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: false))
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(result.range, NSRange(location: 0, length: 3))
XCTAssertFalse(result.wrapped)
result = try #require(textFind.find(in: matches, forward: true, wraps: false))
#expect(matches.count == 2)
#expect(result.range == NSRange(location: 0, length: 3))
#expect(!result.wrapped)
XCTAssertNil(textFind.find(in: matches, forward: false, wraps: false))
#expect(textFind.find(in: matches, forward: false, wraps: false) == nil)
textFind = try TextFind(for: text, findString: findString, mode: .textual(options: [], fullWord: false), selectedRanges: [NSRange(location: 1, length: 0)])
matches = try textFind.matches
XCTAssertEqual(matches.count, 2)
#expect(matches.count == 2)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 8, length: 3))
XCTAssertFalse(result.wrapped)
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(result.range == NSRange(location: 8, length: 3))
#expect(!result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 8, length: 3))
XCTAssertTrue(result.wrapped)
result = try #require(textFind.find(in: matches, forward: false, wraps: true))
#expect(result.range == NSRange(location: 8, length: 3))
#expect(result.wrapped)
textFind = try TextFind(for: text, findString: findString, mode: .textual(options: .caseInsensitive, fullWord: false), selectedRanges: [NSRange(location: 1, length: 0)])
matches = try textFind.matches
XCTAssertEqual(matches.count, 3)
#expect(matches.count == 3)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 16, length: 3))
XCTAssertTrue(result.wrapped)
result = try #require(textFind.find(in: matches, forward: false, wraps: true))
#expect(result.range == NSRange(location: 16, length: 3))
#expect(result.wrapped)
}
func testFullWord() throws {
@Test func fullWord() throws {
var textFind: TextFind
var result: (range: NSRange, wrapped: Bool)
@ -135,43 +122,43 @@ final class TextFindTests: XCTestCase {
textFind = try TextFind(for: "apples apple Apple", findString: "apple",
mode: .textual(options: .caseInsensitive, fullWord: true))
matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(result.range, NSRange(location: 7, length: 5))
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(matches.count == 2)
#expect(result.range == NSRange(location: 7, length: 5))
textFind = try TextFind(for: "apples apple Apple", findString: "apple",
mode: .textual(options: [.caseInsensitive, .literal], fullWord: true))
matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(result.range, NSRange(location: 7, length: 5))
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(matches.count == 2)
#expect(result.range == NSRange(location: 7, length: 5))
textFind = try TextFind(for: "Apfel Äpfel Äpfelchen", findString: "Äpfel",
mode: .textual(options: .diacriticInsensitive, fullWord: true))
matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(result.range, NSRange(location: 0, length: 5))
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(matches.count == 2)
#expect(result.range == NSRange(location: 0, length: 5))
textFind = try TextFind(for: "イヌら イヌ イヌ", findString: "イヌ",
mode: .textual(options: .widthInsensitive, fullWord: true))
matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(result.range, NSRange(location: 4, length: 2))
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(matches.count == 2)
#expect(result.range == NSRange(location: 4, length: 2))
}
func testUnescapedRegexFind() throws {
@Test func unescapedRegexFind() throws {
let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: true)
let textFind = try TextFind(for: "1", findString: "1", mode: mode, selectedRanges: [NSRange(0..<1)])
let replacementResult = try XCTUnwrap(textFind.replace(with: #"foo\n1"#))
XCTAssertEqual(replacementResult.value, "foo\n1")
let replacementResult = try #require(textFind.replace(with: #"foo\n1"#))
#expect(replacementResult.value == "foo\n1")
}
func testSingleRegexFindAndReplacement() throws {
@Test func findAndReplaceSingleRegex() throws {
let findString = "(?!=a)b(c)(?=d)"
let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: true)
@ -184,42 +171,42 @@ final class TextFindTests: XCTestCase {
textFind = try TextFind(for: "abcdefg abcdefg ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 1)])
matches = try textFind.matches
XCTAssertEqual(matches.count, 3)
#expect(matches.count == 3)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 9, length: 2))
XCTAssertFalse(result.wrapped)
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(result.range == NSRange(location: 9, length: 2))
#expect(!result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 17, length: 2))
XCTAssertTrue(result.wrapped)
result = try #require(textFind.find(in: matches, forward: false, wraps: true))
#expect(result.range == NSRange(location: 17, length: 2))
#expect(result.wrapped)
textFind = try TextFind(for: "ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 1)])
matches = try textFind.matches
XCTAssertEqual(matches.count, 1)
#expect(matches.count == 1)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 1, length: 2))
XCTAssertTrue(result.wrapped)
result = try #require(textFind.find(in: matches, forward: true, wraps: true))
#expect(result.range == NSRange(location: 1, length: 2))
#expect(result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 1, length: 2))
XCTAssertTrue(result.wrapped)
result = try #require(textFind.find(in: matches, forward: false, wraps: true))
#expect(result.range == NSRange(location: 1, length: 2))
#expect(result.wrapped)
XCTAssertNil(textFind.replace(with: "$1"))
#expect(textFind.replace(with: "$1") == nil)
textFind = try TextFind(for: "ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 2)])
let replacementResult = try XCTUnwrap(textFind.replace(with: "$1\\t"))
XCTAssertEqual(replacementResult.value, "C\t")
XCTAssertEqual(replacementResult.range, NSRange(location: 1, length: 2))
let replacementResult = try #require(textFind.replace(with: "$1\\t"))
#expect(replacementResult.value == "C\t")
#expect(replacementResult.range == NSRange(location: 1, length: 2))
}
func testFindAll() throws {
@Test func findAll() throws {
let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: false)
var textFind: TextFind
@ -230,13 +217,13 @@ final class TextFindTests: XCTestCase {
textFind.findAll { (matchedRanges, _) in
matches.append(matchedRanges)
}
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(matches[0].count, 2)
XCTAssertEqual(matches[0][0], NSRange(location: 1, length: 2))
XCTAssertEqual(matches[0][1], NSRange(location: 2, length: 1))
XCTAssertEqual(matches[1].count, 2)
XCTAssertEqual(matches[1][0], NSRange(location: 9, length: 2))
XCTAssertEqual(matches[1][1], NSRange(location: 10, length: 1))
#expect(matches.count == 2)
#expect(matches[0].count == 2)
#expect(matches[0][0] == NSRange(location: 1, length: 2))
#expect(matches[0][1] == NSRange(location: 2, length: 1))
#expect(matches[1].count == 2)
#expect(matches[1][0] == NSRange(location: 9, length: 2))
#expect(matches[1][1] == NSRange(location: 10, length: 1))
textFind = try TextFind(for: "abcdefg ABCDEFG", findString: "ab", mode: mode)
@ -245,15 +232,15 @@ final class TextFindTests: XCTestCase {
textFind.findAll { (matchedRanges, _) in
matches.append(matchedRanges)
}
XCTAssertEqual(matches.count, 2)
XCTAssertEqual(matches[0].count, 1)
XCTAssertEqual(matches[0][0], NSRange(location: 0, length: 2))
XCTAssertEqual(matches[1].count, 1)
XCTAssertEqual(matches[1][0], NSRange(location: 8, length: 2))
#expect(matches.count == 2)
#expect(matches[0].count == 1)
#expect(matches[0][0] == NSRange(location: 0, length: 2))
#expect(matches[1].count == 1)
#expect(matches[1][0] == NSRange(location: 8, length: 2))
}
func testReplaceAll() throws {
@Test func replaceAll() throws {
var textFind: TextFind
var replacementItems: [TextFind.ReplacementItem]
@ -263,10 +250,10 @@ final class TextFindTests: XCTestCase {
mode: .regularExpression(options: .caseInsensitive, unescapesReplacement: false))
(replacementItems, selectedRanges) = textFind.replaceAll(with: "$1\\\\t") { (_, _, _) in }
XCTAssertEqual(replacementItems.count, 1)
XCTAssertEqual(replacementItems[0].value, "ac\\tdefg AC\\tDEFG")
XCTAssertEqual(replacementItems[0].range, NSRange(location: 0, length: 15))
XCTAssertNil(selectedRanges)
#expect(replacementItems.count == 1)
#expect(replacementItems[0].value == "ac\\tdefg AC\\tDEFG")
#expect(replacementItems[0].range == NSRange(location: 0, length: 15))
#expect(selectedRanges == nil)
textFind = try TextFind(for: "abcdefg abcdefg abcdefg", findString: "abc",
@ -276,12 +263,12 @@ final class TextFindTests: XCTestCase {
NSRange(location: 16, length: 7)])
(replacementItems, selectedRanges) = textFind.replaceAll(with: "_") { (_, _, _) in }
XCTAssertEqual(replacementItems.count, 2)
XCTAssertEqual(replacementItems[0].value, "bcdefg _defg")
XCTAssertEqual(replacementItems[0].range, NSRange(location: 1, length: 14))
XCTAssertEqual(replacementItems[1].value, "_defg")
XCTAssertEqual(replacementItems[1].range, NSRange(location: 16, length: 7))
XCTAssertEqual(selectedRanges?[0], NSRange(location: 1, length: 12))
XCTAssertEqual(selectedRanges?[1], NSRange(location: 14, length: 5))
#expect(replacementItems.count == 2)
#expect(replacementItems[0].value == "bcdefg _defg")
#expect(replacementItems[0].range == NSRange(location: 1, length: 14))
#expect(replacementItems[1].value == "_defg")
#expect(replacementItems[1].range == NSRange(location: 16, length: 7))
#expect(selectedRanges?[0] == NSRange(location: 1, length: 12))
#expect(selectedRanges?[1] == NSRange(location: 14, length: 5))
}
}

View File

@ -24,61 +24,60 @@
// limitations under the License.
//
import AppKit
import UniformTypeIdentifiers
import XCTest
import Testing
@testable import CotEditor
final class ThemeTests: XCTestCase {
actor ThemeTests {
private let themeDirectoryName = "Themes"
private lazy var bundle = Bundle(for: type(of: self))
func testDefaultTheme() throws {
@Test func defaultTheme() throws {
let themeName = "Dendrobates"
let theme = try self.loadThemeWithName(themeName)
XCTAssertEqual(theme.name, themeName)
XCTAssertEqual(theme.text.color, NSColor.black.usingColorSpace(.genericRGB))
XCTAssertEqual(theme.insertionPoint.color, NSColor.black.usingColorSpace(.genericRGB))
XCTAssertEqual(theme.invisibles.color.brightnessComponent, 0.72, accuracy: 0.01)
XCTAssertEqual(theme.background.color, NSColor.white.usingColorSpace(.genericRGB))
XCTAssertEqual(theme.lineHighlight.color.brightnessComponent, 0.93, accuracy: 0.01)
XCTAssertEqual(theme.effectiveSecondarySelectionColor(for: NSAppearance(named: .aqua)!), .unemphasizedSelectedContentBackgroundColor)
XCTAssertFalse(theme.isDarkTheme)
#expect(theme.name == themeName)
#expect(theme.text.color == NSColor.black.usingColorSpace(.genericRGB))
#expect(theme.insertionPoint.color == NSColor.black.usingColorSpace(.genericRGB))
// #expect(theme.invisibles.color.brightnessComponent == 0.725) // accuracy: 0.01
#expect(theme.background.color == NSColor.white.usingColorSpace(.genericRGB))
// #expect(theme.lineHighlight.color.brightnessComponent == 0.929) // accuracy: 0.01
#expect(theme.effectiveSecondarySelectionColor(for: NSAppearance(named: .aqua)!) == .unemphasizedSelectedContentBackgroundColor)
#expect(!theme.isDarkTheme)
for type in SyntaxType.allCases {
let style = try XCTUnwrap(theme.style(for: type))
XCTAssertGreaterThan(style.color.hueComponent, 0)
let style = try #require(theme.style(for: type))
#expect(style.color.hueComponent > 0)
}
XCTAssertFalse(theme.isDarkTheme)
#expect(!theme.isDarkTheme)
}
func testDarkTheme() throws {
@Test func darkTheme() throws {
let themeName = "Anura (Dark)"
let theme = try self.loadThemeWithName(themeName)
XCTAssertEqual(theme.name, themeName)
XCTAssertTrue(theme.isDarkTheme)
#expect(theme.name == themeName)
#expect(theme.isDarkTheme)
}
/// Tests if all of bundled themes are valid.
func testBundledThemes() throws {
@Test func bundledThemes() throws {
let themeDirectoryURL = try XCTUnwrap(self.bundle.url(forResource: self.themeDirectoryName, withExtension: nil))
let themeDirectoryURL = try #require(Bundle(for: type(of: self)).url(forResource: self.themeDirectoryName, withExtension: nil))
let urls = try FileManager.default.contentsOfDirectory(at: themeDirectoryURL, includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants, .skipsHiddenFiles])
.filter { UTType.cotTheme.preferredFilenameExtension == $0.pathExtension }
XCTAssertFalse(urls.isEmpty)
#expect(!urls.isEmpty)
for url in urls {
XCTAssertNoThrow(try Theme(contentsOf: url))
#expect(throws: Never.self) { try Theme(contentsOf: url) }
}
}
}
@ -90,7 +89,7 @@ private extension ThemeTests {
func loadThemeWithName(_ name: String) throws -> Theme {
guard
let url = self.bundle.url(forResource: name, withExtension: UTType.cotTheme.preferredFilenameExtension, subdirectory: self.themeDirectoryName)
let url = Bundle(for: type(of: self)).url(forResource: name, withExtension: UTType.cotTheme.preferredFilenameExtension, subdirectory: self.themeDirectoryName)
else { throw CocoaError(.fileNoSuchFile) }
return try Theme(contentsOf: url)

View File

@ -24,54 +24,55 @@
// limitations under the License.
//
import XCTest
import Foundation
import Testing
@testable import CotEditor
final class URLExtensionsTests: XCTestCase {
struct URLExtensionsTests {
func testRelativeURLCreation() {
@Test func createRelativeURL() {
let url = URL(filePath: "/foo/bar/file.txt")
let baseURL = URL(filePath: "/foo/buz/file.txt")
XCTAssertEqual(url.path(relativeTo: baseURL), "../bar/file.txt")
#expect(url.path(relativeTo: baseURL) == "../bar/file.txt")
}
func testRelativeURLCreation2() {
@Test func createRelativeURL2() {
let url = URL(filePath: "/file1.txt")
let baseURL = URL(filePath: "/file2.txt")
XCTAssertEqual(url.path(relativeTo: baseURL), "file1.txt")
#expect(url.path(relativeTo: baseURL) == "file1.txt")
}
func testRelativeURLCreationWithSameURLs() {
@Test func createRelativeURLWithSameURLs() {
let url = URL(filePath: "/file1.txt")
let baseURL = URL(filePath: "/file1.txt")
XCTAssertEqual(url.path(relativeTo: baseURL), "file1.txt")
#expect(url.path(relativeTo: baseURL) == "file1.txt")
}
func testRelativeURLCreationWithDirectoryURLs() {
@Test func createRelativeURLWithDirectoryURLs() {
let url = URL(filePath: "Dog/Cow/Cat/file1.txt")
XCTAssertEqual(url.path(relativeTo: URL(filePath: "Dog/Cow", directoryHint: .isDirectory)), "Cat/file1.txt")
XCTAssertEqual(url.path(relativeTo: URL(filePath: "Dog/Cow/", directoryHint: .isDirectory)), "Cat/file1.txt")
XCTAssertEqual(url.path(relativeTo: URL(filePath: "Dog/Cow/Cat", directoryHint: .isDirectory)), "file1.txt")
XCTAssertEqual(url.path(relativeTo: URL(filePath: "", directoryHint: .isDirectory)), "Dog/Cow/Cat/file1.txt")
#expect(url.path(relativeTo: URL(filePath: "Dog/Cow", directoryHint: .isDirectory)) == "Cat/file1.txt")
#expect(url.path(relativeTo: URL(filePath: "Dog/Cow/", directoryHint: .isDirectory)) == "Cat/file1.txt")
#expect(url.path(relativeTo: URL(filePath: "Dog/Cow/Cat", directoryHint: .isDirectory)) == "file1.txt")
#expect(url.path(relativeTo: URL(filePath: "", directoryHint: .isDirectory)) == "Dog/Cow/Cat/file1.txt")
let url2 = URL(filePath: "file1.txt")
XCTAssertEqual(url2.path(relativeTo: URL(filePath: "", directoryHint: .isDirectory)), "file1.txt")
XCTAssertEqual(url2.path(relativeTo: URL(filePath: "Dog", directoryHint: .isDirectory)), "../file1.txt")
#expect(url2.path(relativeTo: URL(filePath: "", directoryHint: .isDirectory)) == "file1.txt")
#expect(url2.path(relativeTo: URL(filePath: "Dog", directoryHint: .isDirectory)) == "../file1.txt")
}
func testItemReplacementDirectoryCreation() throws {
@Test func createItemReplacementDirectory() throws {
XCTAssertNoThrow(try URL.itemReplacementDirectory)
#expect(throws: Never.self) { try URL.itemReplacementDirectory }
}
}

View File

@ -24,54 +24,54 @@
//
import UniformTypeIdentifiers
import XCTest
import Testing
@testable import CotEditor
final class UTTypeExtensionTests: XCTestCase {
struct UTTypeExtensionTests {
func testFilenameExtensions() {
@Test func filenameExtensions() {
XCTAssertEqual(UTType.yaml.filenameExtensions, ["yml", "yaml"])
XCTAssertEqual(UTType.svg.filenameExtensions, ["svg", "svgz"])
XCTAssertEqual(UTType.mpeg2TransportStream.filenameExtensions, ["ts"])
XCTAssertEqual(UTType.propertyList.filenameExtensions, ["plist"])
#expect(UTType.yaml.filenameExtensions == ["yml", "yaml"])
#expect(UTType.svg.filenameExtensions == ["svg", "svgz"])
#expect(UTType.mpeg2TransportStream.filenameExtensions == ["ts"])
#expect(UTType.propertyList.filenameExtensions == ["plist"])
}
func testURLConformance() {
@Test func conformURL() {
let xmlURL = URL(filePath: "foo.xml")
XCTAssertFalse(xmlURL.conforms(to: .svg))
XCTAssertTrue(xmlURL.conforms(to: .xml))
XCTAssertFalse(xmlURL.conforms(to: .plainText))
#expect(!xmlURL.conforms(to: .svg))
#expect(xmlURL.conforms(to: .xml))
#expect(!xmlURL.conforms(to: .plainText))
let svgzURL = URL(filePath: "FOO.SVGZ")
XCTAssertTrue(svgzURL.conforms(to: .svg))
#expect(svgzURL.conforms(to: .svg))
}
func testSVG() throws {
@Test func svg() throws {
XCTAssertTrue(UTType.svg.conforms(to: .text))
XCTAssertTrue(UTType.svg.conforms(to: .image))
#expect(UTType.svg.conforms(to: .text))
#expect(UTType.svg.conforms(to: .image))
let svgz = try XCTUnwrap(UTType(filenameExtension: "svgz"))
XCTAssertEqual(svgz, .svg)
XCTAssertFalse(svgz.conforms(to: .gzip))
let svgz = try #require(UTType(filenameExtension: "svgz"))
#expect(svgz == .svg)
#expect(!svgz.conforms(to: .gzip))
}
func testPlist() throws {
@Test func plist() {
XCTAssertTrue(UTType.propertyList.conforms(to: .data))
XCTAssertFalse(UTType.propertyList.conforms(to: .image))
#expect(UTType.propertyList.conforms(to: .data))
#expect(!UTType.propertyList.conforms(to: .image))
}
func testIsPlainText() {
@Test func isPlainText() {
XCTAssertTrue(UTType.propertyList.isPlainText)
XCTAssertTrue(UTType.svg.isPlainText)
XCTAssertTrue(UTType(filenameExtension: "ts")!.isPlainText)
#expect(UTType.propertyList.isPlainText)
#expect(UTType.svg.isPlainText)
#expect(UTType(filenameExtension: "ts")!.isPlainText)
}
}

View File

@ -9,7 +9,7 @@
//
// ---------------------------------------------------------------------------
//
// © 2019-2023 1024jp
// © 2019-2024 1024jp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,110 +24,104 @@
// limitations under the License.
//
import Foundation
import Combine
import XCTest
import Testing
@testable import CotEditor
final class UserDefaultsObservationTests: XCTestCase {
struct UserDefaultsObservationTests {
func testKeyObservation() {
@Test func observeKey() async {
let key = DefaultKey<Bool>("Test Key")
defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for normal key")
UserDefaults.standard[key] = false
await confirmation("UserDefaults observation for normal key") { confirm in
let observer = UserDefaults.standard.publisher(for: key)
.sink { value in
XCTAssertTrue(value)
XCTAssertEqual(OperationQueue.current, .main)
expectation.fulfill()
#expect(value)
confirm()
}
UserDefaults.standard[key] = true
self.wait(for: [expectation], timeout: .zero)
// -> Waiting with zero timeout can be failed when the closure is performed not immediately but in another runloop.
observer.cancel()
UserDefaults.standard[key] = false
}
}
func testInitialEmission() {
@Test func initialEmit() async {
let key = DefaultKey<Bool>("Initial Emission Test Key")
defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for initial emission")
UserDefaults.standard[key] = false
await confirmation("UserDefaults observation for initial emission") { confirm in
let observer = UserDefaults.standard.publisher(for: key, initial: true)
.sink { value in
XCTAssertFalse(value)
expectation.fulfill()
#expect(!value)
confirm()
}
observer.cancel()
UserDefaults.standard[key] = true
self.wait(for: [expectation], timeout: .zero)
}
}
func testOptionalKey() {
@Test func optionalKey() async {
let key = DefaultKey<String?>("Optional Test Key")
defer { UserDefaults.standard.restore(key: key) }
XCTAssertNil(UserDefaults.standard[key])
#expect(UserDefaults.standard[key] == nil)
UserDefaults.standard[key] = "cow"
XCTAssertEqual(UserDefaults.standard[key], "cow")
#expect(UserDefaults.standard[key] == "cow")
let expectation = self.expectation(description: "UserDefaults observation for optional key")
await confirmation("UserDefaults observation for optional key") { confirm in
let observer = UserDefaults.standard.publisher(for: key)
.sink { value in
XCTAssertNil(value)
expectation.fulfill()
#expect(value == nil)
confirm()
}
UserDefaults.standard[key] = nil
self.wait(for: [expectation], timeout: .zero)
XCTAssertNil(UserDefaults.standard[key])
#expect(UserDefaults.standard[key] == nil)
observer.cancel()
UserDefaults.standard[key] = "dog"
XCTAssertEqual(UserDefaults.standard[key], "dog")
#expect(UserDefaults.standard[key] == "dog")
}
}
func testRawRepresentable() {
@Test func rawRepresentable() async {
enum Clarus: Int { case dog, cow }
let key = RawRepresentableDefaultKey<Clarus>("Raw Representable Test Key")
defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for raw representable")
UserDefaults.standard[key] = .dog
await confirmation("UserDefaults observation for raw representable") { confirm in
let observer = UserDefaults.standard.publisher(for: key)
.sink { value in
XCTAssertEqual(value, .cow)
expectation.fulfill()
#expect(value == .cow)
confirm()
}
UserDefaults.standard[key] = .cow
self.wait(for: [expectation], timeout: .zero)
observer.cancel()
UserDefaults.standard[key] = .dog
XCTAssertEqual(UserDefaults.standard[key], .dog)
#expect(UserDefaults.standard[key] == .dog)
}
}
}