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] Suppress display of the “Extracting” message on the navigation bar in instantaneous parsing.
- [trivial] Make names of code contributors in the About window selectable. - [trivial] Make names of code contributors in the About window selectable.
- [dev] Update the build environment to Xcode 16. - [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. - [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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,14 +23,14 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class ArithmeticsTests: XCTestCase { struct ArithmeticsTests {
func testDigits() { @Test func digits() {
XCTAssertEqual(0.digits, [0]) #expect(0.digits == [0])
XCTAssertEqual(1024.digits, [4, 2, 0, 1]) #expect(1024.digits == [4, 2, 0, 1])
} }
} }

View File

@ -24,69 +24,64 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class BracePairTests: XCTestCase { struct BracePairTests {
func testIndexFind() { @Test func findIndex() {
let string = "if < foo < 🐕 > > else < >" let string = "if < foo < 🐕 > > else < >"
let pair = BracePair("<", ">") let pair = BracePair("<", ">")
XCTAssertEqual(string.indexOfBracePair(endIndex: string.index(14), pair: pair), string.index(3)) #expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair) == string.index(3))
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(4), pair: pair), string.index(15)) #expect(string.indexOfBracePair(beginIndex: string.index(4), pair: pair) == string.index(15))
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(2), pair: pair)) #expect(string.indexOfBracePair(endIndex: string.index(2), pair: pair) == nil)
XCTAssertNil(string.indexOfBracePair(beginIndex: string.index(2), pair: .ltgt)) #expect(string.indexOfBracePair(beginIndex: string.index(2), pair: .ltgt) == nil)
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(14), pair: pair, until: string.index(15))) #expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair, until: string.index(15)) == nil)
XCTAssertNil(string.indexOfBracePair(beginIndex: string.index(4), pair: pair, until: string.index(2))) #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 string = "if ' foo ' 🐕 ' ' else ' '"
let pair = BracePair("'", "'") let pair = BracePair("'", "'")
XCTAssertEqual(string.indexOfBracePair(endIndex: string.index(14), pair: pair), string.index(13)) #expect(string.indexOfBracePair(endIndex: string.index(14), pair: pair) == string.index(13))
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(4), pair: pair), string.index(9)) #expect(string.indexOfBracePair(beginIndex: string.index(4), pair: pair) == string.index(9))
XCTAssertNil(string.indexOfBracePair(endIndex: string.index(2), pair: pair)) #expect(string.indexOfBracePair(endIndex: string.index(2), pair: pair) == nil)
XCTAssertEqual(string.indexOfBracePair(beginIndex: string.index(2), pair: pair), string.index(3)) #expect(string.indexOfBracePair(beginIndex: string.index(2), pair: pair) == string.index(3))
} }
func testScanner() { @Test func scan() {
let string = "def { foo {} | { bar } } " let string = "def { foo {} | { bar } } "
let pairs = BracePair.braces let pairs = BracePair.braces
XCTAssertNil(string.rangeOfEnclosingBracePair(at: string.range(1..<2), candidates: pairs)) #expect(string.rangeOfEnclosingBracePair(at: string.range(1..<2), candidates: pairs) == nil)
XCTAssertNil(string.rangeOfEnclosingBracePair(at: string.range(24..<24), candidates: pairs)) #expect(string.rangeOfEnclosingBracePair(at: string.range(24..<24), candidates: pairs) == nil)
XCTAssertEqual(string.rangeOfEnclosingBracePair(at: string.range(13..<14), candidates: pairs), // = | #expect(string.rangeOfEnclosingBracePair(at: string.range(13..<14), candidates: pairs) == string.range(4..<24)) // = |
string.range(4..<24))
XCTAssertEqual(string.rangeOfEnclosingBracePair(at: string.range(11..<11), candidates: pairs), // = {} #expect(string.rangeOfEnclosingBracePair(at: string.range(11..<11), candidates: pairs) == string.range(10..<12)) // = {}
string.range(10..<12))
} }
func testScannerWithEscape() { @Test func scanWithEscape() {
let pairs = BracePair.braces let pairs = BracePair.braces
let string1 = #"foo (\() )"# let string1 = #"foo (\() )"#
XCTAssertEqual(string1.rangeOfEnclosingBracePair(at: string1.range(7..<7), candidates: pairs), #expect(string1.rangeOfEnclosingBracePair(at: string1.range(7..<7), candidates: pairs) == string1.range(4..<8))
string1.range(4..<8))
let string2 = #"foo (\\() )"# let string2 = #"foo (\\() )"#
XCTAssertEqual(string2.rangeOfEnclosingBracePair(at: string2.range(8..<8), candidates: pairs), #expect(string2.rangeOfEnclosingBracePair(at: string2.range(8..<8), candidates: pairs) == string2.range(7..<9))
string2.range(7..<9))
let string3 = #"foo (\\\() )"# let string3 = #"foo (\\\() )"#
XCTAssertEqual(string3.rangeOfEnclosingBracePair(at: string3.range(9..<9), candidates: pairs), #expect(string3.rangeOfEnclosingBracePair(at: string3.range(9..<9), candidates: pairs) == string3.range(4..<10))
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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,131 +24,132 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit
import Testing
@testable import CotEditor @testable import CotEditor
final class CharacterInfoTests: XCTestCase { struct CharacterInfoTests {
// MARK: UTF32.CodeUnit Extension Tests // MARK: UTF32.CodeUnit Extension Tests
func testSingleSurrogate() { @Test func singleSurrogate() {
let character: UTF32.CodeUnit = 0xD83D let character: UTF32.CodeUnit = 0xD83D
XCTAssertEqual(character.unicodeName, "<lead surrogate-D83D>") #expect(character.unicodeName == "<lead surrogate-D83D>")
XCTAssertEqual(character.blockName, "High Surrogates") #expect(character.blockName == "High Surrogates")
XCTAssertNil(Unicode.Scalar(character)) #expect(Unicode.Scalar(character) == nil)
} }
// MARK: - UnicodeCharacter Tests // MARK: - UnicodeCharacter Tests
func testSingleChar() { @Test func singleChar() {
let unicode = Unicode.Scalar("") let unicode = Unicode.Scalar("")
XCTAssertEqual(unicode.codePoint, "U+3042") #expect(unicode.codePoint == "U+3042")
XCTAssertFalse(unicode.isSurrogatePair) #expect(!unicode.isSurrogatePair)
XCTAssertNil(unicode.surrogateCodePoints) #expect(unicode.surrogateCodePoints == nil)
XCTAssertEqual(unicode.name, "HIRAGANA LETTER A") #expect(unicode.name == "HIRAGANA LETTER A")
XCTAssertEqual(unicode.blockName, "Hiragana") #expect(unicode.blockName == "Hiragana")
XCTAssertNotNil(unicode.localizedBlockName) #expect(unicode.localizedBlockName != nil)
} }
func testSurrogateEmoji() { @Test func surrogateEmoji() {
let unicode = Unicode.Scalar("😀") let unicode = Unicode.Scalar("😀")
XCTAssertEqual(unicode.codePoint, "U+1F600") #expect(unicode.codePoint == "U+1F600")
XCTAssertTrue(unicode.isSurrogatePair) #expect(unicode.isSurrogatePair)
XCTAssertEqual(unicode.surrogateCodePoints?.lead, "U+D83D") #expect(unicode.surrogateCodePoints?.lead == "U+D83D")
XCTAssertEqual(unicode.surrogateCodePoints?.trail, "U+DE00") #expect(unicode.surrogateCodePoints?.trail == "U+DE00")
XCTAssertEqual(unicode.name, "GRINNING FACE") #expect(unicode.name == "GRINNING FACE")
XCTAssertEqual(unicode.blockName, "Emoticons") #expect(unicode.blockName == "Emoticons")
XCTAssertNotNil(unicode.localizedBlockName) #expect(unicode.localizedBlockName != nil)
} }
func testUnicodeBlockNameWithHyphen() { @Test func unicodeBlockNameWithHyphen() {
let character = Unicode.Scalar("") let character = Unicode.Scalar("")
XCTAssertEqual(character.codePoint, "U+FDFD") #expect(character.codePoint == "U+FDFD")
XCTAssertEqual(character.name, "ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM") #expect(character.name == "ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM")
XCTAssertEqual(character.localizedBlockName, "Arabic Presentation Forms-A") #expect(character.localizedBlockName == "Arabic Presentation Forms-A")
} }
func testUnicodeControlPictures() throws { @Test func unicodeControlPictures() throws {
// test NULL // test NULL
let nullCharacter = try XCTUnwrap(Unicode.Scalar(0x0000)) let nullCharacter = try #require(Unicode.Scalar(0x0000))
let nullPictureCharacter = try XCTUnwrap(Unicode.Scalar(0x2400)) let nullPictureCharacter = try #require(Unicode.Scalar(0x2400))
XCTAssertEqual(nullCharacter.name, "NULL") #expect(nullCharacter.name == "NULL")
XCTAssertEqual(nullPictureCharacter.name, "SYMBOL FOR NULL") #expect(nullPictureCharacter.name == "SYMBOL FOR NULL")
XCTAssertEqual(nullCharacter.pictureRepresentation, nullPictureCharacter) #expect(nullCharacter.pictureRepresentation == nullPictureCharacter)
// test SPACE // test SPACE
let spaceCharacter = try XCTUnwrap(Unicode.Scalar(0x0020)) let spaceCharacter = try #require(Unicode.Scalar(0x0020))
let spacePictureCharacter = try XCTUnwrap(Unicode.Scalar(0x2420)) let spacePictureCharacter = try #require(Unicode.Scalar(0x2420))
XCTAssertEqual(spaceCharacter.name, "SPACE") #expect(spaceCharacter.name == "SPACE")
XCTAssertEqual(spacePictureCharacter.name, "SYMBOL FOR SPACE") #expect(spacePictureCharacter.name == "SYMBOL FOR SPACE")
XCTAssertEqual(spaceCharacter.pictureRepresentation, spacePictureCharacter) #expect(spaceCharacter.pictureRepresentation == spacePictureCharacter)
// test DELETE // test DELETE
let deleteCharacter = try XCTUnwrap(Unicode.Scalar(NSDeleteCharacter)) let deleteCharacter = try #require(Unicode.Scalar(NSDeleteCharacter))
let deletePictureCharacter = Unicode.Scalar("") let deletePictureCharacter = Unicode.Scalar("")
XCTAssertEqual(deleteCharacter.name, "DELETE") #expect(deleteCharacter.name == "DELETE")
XCTAssertEqual(deletePictureCharacter.name, "SYMBOL FOR DELETE") #expect(deletePictureCharacter.name == "SYMBOL FOR DELETE")
XCTAssertEqual(deleteCharacter.pictureRepresentation, deletePictureCharacter) #expect(deleteCharacter.pictureRepresentation == deletePictureCharacter)
// test one after the last C0 control character // test one after the last C0 control character
let exclamationCharacter = try XCTUnwrap(Unicode.Scalar(0x0021)) let exclamationCharacter = try #require(Unicode.Scalar(0x0021))
XCTAssertEqual(exclamationCharacter.name, "EXCLAMATION MARK") #expect(exclamationCharacter.name == "EXCLAMATION MARK")
XCTAssertNil(exclamationCharacter.pictureRepresentation) #expect(exclamationCharacter.pictureRepresentation == nil)
} }
// MARK: - CharacterInfo Tests // MARK: - CharacterInfo Tests
func testSingleCharWithVSInfo() { @Test func singleCharacterWithVSInfo() {
let charInfo = CharacterInfo(character: "☺︎") let charInfo = CharacterInfo(character: "☺︎")
XCTAssertEqual(charInfo.character, "☺︎") #expect(charInfo.character == "☺︎")
XCTAssertFalse(charInfo.isComplex) #expect(!charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+263A", "U+FE0E"]) #expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+263A", "U+FE0E"])
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.name), ["WHITE SMILING FACE", "VARIATION SELECTOR-15"]) #expect(charInfo.character.unicodeScalars.map(\.name) == ["WHITE SMILING FACE", "VARIATION SELECTOR-15"])
XCTAssertEqual(charInfo.localizedDescription, "WHITE SMILING FACE (Text Style)") #expect(charInfo.localizedDescription == "WHITE SMILING FACE (Text Style)")
} }
func testCombiningCharacterInfo() { @Test func combiningCharacterInfo() {
let charInfo = CharacterInfo(character: "1") let charInfo = CharacterInfo(character: "1")
XCTAssertTrue(charInfo.isComplex) #expect(charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+0031", "U+FE0F", "U+20E3"]) #expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+0031", "U+FE0F", "U+20E3"])
XCTAssertEqual(charInfo.localizedDescription, "<a letter consisting of 3 characters>") #expect(charInfo.localizedDescription == "<a letter consisting of 3 characters>")
} }
func testNationalIndicatorInfo() { @Test func nationalIndicatorInfo() {
let charInfo = CharacterInfo(character: "🇯🇵") let charInfo = CharacterInfo(character: "🇯🇵")
XCTAssertTrue(charInfo.isComplex) #expect(charInfo.isComplex)
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.codePoint), ["U+1F1EF", "U+1F1F5"]) #expect(charInfo.character.unicodeScalars.map(\.codePoint) == ["U+1F1EF", "U+1F1F5"])
} }
func testControlCharacterInfo() { @Test func controlCharacterInfo() {
let charInfo = CharacterInfo(character: " ") let charInfo = CharacterInfo(character: " ")
XCTAssertEqual(charInfo.character, " ") #expect(charInfo.character == " ")
XCTAssertEqual(charInfo.pictureCharacter, "") #expect(charInfo.pictureCharacter == "")
XCTAssertEqual(charInfo.character.unicodeScalars.map(\.name), ["SPACE"]) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,104 +24,100 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class CollectionTests: XCTestCase { struct CollectionTests {
func testAppendUnique() { @Test func appendUnique() {
var array = [0, 1, 2, 3, 4] var array = [0, 1, 2, 3, 4]
array.appendUnique(0, maximum: 5) array.appendUnique(0, maximum: 5)
XCTAssertEqual(array, [1, 2, 3, 4, 0]) #expect(array == [1, 2, 3, 4, 0])
array.appendUnique(6, maximum: 5) array.appendUnique(6, maximum: 5)
XCTAssertEqual(array, [2, 3, 4, 0, 6]) #expect(array == [2, 3, 4, 0, 6])
array.appendUnique(7, maximum: 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) 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) #expect([1, 2, 0, -1, 3].count(where: { $0 > 0 }) == 3)
XCTAssertEqual([0, 1, 2, 0, -1].count(where: { $0 > 0 }), 2) #expect([0, 1, 2, 0, -1].count(where: { $0 > 0 }) == 2)
XCTAssertEqual([1, 2, 3, 4, 5].count(where: { $0 > 0 }), 5) #expect([1, 2, 3, 4, 5].count(where: { $0 > 0 }) == 5)
XCTAssertEqual([1, 2, 0, -1, 3].countPrefix(while: { $0 > 0 }), 2) #expect([1, 2, 0, -1, 3].countPrefix(while: { $0 > 0 }) == 2)
XCTAssertEqual([0, 1, 2, 0, -1].countPrefix(while: { $0 > 0 }), 0) #expect([0, 1, 2, 0, -1].countPrefix(while: { $0 > 0 }) == 0)
XCTAssertEqual([1, 2, 3, 4, 5].countPrefix(while: { $0 > 0 }), 5) #expect([1, 2, 3, 4, 5].countPrefix(while: { $0 > 0 }) == 5)
} }
func testCountComparison() { @Test func compareCount() {
XCTAssertEqual("".compareCount(with: 0), .equal) #expect("".compareCount(with: 0) == .equal)
XCTAssertEqual("".compareCount(with: 1), .less) #expect("".compareCount(with: 1) == .less)
XCTAssertEqual("a".compareCount(with: 1), .equal) #expect("a".compareCount(with: 1) == .equal)
XCTAssertEqual("🐕".compareCount(with: 1), .equal) #expect("🐕".compareCount(with: 1) == .equal)
XCTAssertEqual("🐕‍🦺".compareCount(with: 1), .equal) #expect("🐕‍🦺".compareCount(with: 1) == .equal)
XCTAssertEqual("🐶🐱".compareCount(with: 3), .less) #expect("🐶🐱".compareCount(with: 3) == .less)
XCTAssertEqual("🐶🐱".compareCount(with: 2), .equal) #expect("🐶🐱".compareCount(with: 2) == .equal)
XCTAssertEqual("🐶🐱".compareCount(with: 1), .greater) #expect("🐶🐱".compareCount(with: 1) == .greater)
} }
func testKeyMapping() { @Test func mapKeys() {
let dict = [1: 1, 2: 2, 3: 3] let dict = [1: 1, 2: 2, 3: 3]
let mapped = dict.mapKeys { String($0 * 10) } 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 { enum TestKey: String {
case dog, cat, cow case dog, cat, cow
} }
var dict = ["dog": "🐶", "cat": "🐱"] var dict = ["dog": "🐶", "cat": "🐱"]
XCTAssertEqual(dict[TestKey.dog], dict[TestKey.dog.rawValue]) #expect(dict[TestKey.dog] == dict[TestKey.dog.rawValue])
XCTAssertNil(dict[TestKey.cow]) #expect(dict[TestKey.cow] == nil)
dict[TestKey.cow] = "🐮" 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) }
var array: [Int] = (0..<10).map { _ in .random(in: 0..<100) } let sorted = array.sorted { $0 < $1 }
let sorted = array.sorted { $0 < $1 }
#expect(array.sorted() == sorted)
XCTAssertEqual(array.sorted(), sorted)
array.sort()
array.sort() #expect(array == sorted)
XCTAssertEqual(array, sorted)
}
} }
func testBinarySearch() { @Test(arguments: 0..<10) func binarySearch(index: Int) {
let array = (0..<20).map { _ in Int.random(in: 0..<100) }.sorted()
for _ in 0..<10 { for _ in 0..<10 {
let array = (0..<20).map { _ in Int.random(in: 0..<100) }.sorted() let index = Int.random(in: 0..<100)
#expect(array.binarySearchedFirstIndex(where: { $0 > index }) ==
for _ in 0..<10 { array.firstIndex(where: { $0 > index }))
let index = Int.random(in: 0..<100)
XCTAssertEqual(array.binarySearchedFirstIndex(where: { $0 > index }),
array.firstIndex(where: { $0 > index }))
}
} }
} }
} }

View File

@ -23,26 +23,27 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class ComparableTests: XCTestCase { struct ComparableTests {
func testClamp() { @Test func clamp() {
XCTAssertEqual((-2).clamped(to: -10...10), -2) #expect((-2).clamped(to: -10...10) == -2)
XCTAssertEqual(5.clamped(to: 6...10), 6) #expect(5.clamped(to: 6...10) == 6)
XCTAssertEqual(20.clamped(to: 6...10), 10) #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 { struct Item: Equatable {
@ -58,7 +59,7 @@ final class ComparableTests: XCTestCase {
Item(id: 4, bool: true), Item(id: 4, bool: true),
] ]
XCTAssertEqual(items.sorted(\.bool), [ #expect(items.sorted(\.bool) == [
Item(id: 1, bool: true), Item(id: 1, bool: true),
Item(id: 2, bool: true), Item(id: 2, bool: true),
Item(id: 4, bool: true), Item(id: 4, bool: true),
@ -66,7 +67,7 @@ final class ComparableTests: XCTestCase {
Item(id: 3, bool: false), Item(id: 3, bool: false),
]) ])
XCTAssertEqual(items.sorted(using: [KeyPathComparator(\.bool)]), [ #expect(items.sorted(using: [KeyPathComparator(\.bool)]) == [
Item(id: 1, bool: true), Item(id: 1, bool: true),
Item(id: 2, bool: true), Item(id: 2, bool: true),
Item(id: 4, 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,71 +23,55 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class DebouncerTests: XCTestCase { struct DebouncerTests {
func testDebounce() { @Test func debounce() async throws {
let expectation = self.expectation(description: "Debouncer executed") try await confirmation("Debouncer executed", expectedCount: 1) { confirm in
let waitingExpectation = self.expectation(description: "Debouncer waiting") let debouncer = Debouncer(delay: .seconds(0.5)) {
waitingExpectation.isInverted = true confirm()
}
var value = 0
let debouncer = Debouncer(delay: .seconds(0.5)) { debouncer.schedule()
value += 1 debouncer.schedule()
expectation.fulfill()
waitingExpectation.fulfill() try await Task.sleep(for: .seconds(1))
} }
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)
} }
func testImidiateFire() { @Test func immediateFire() {
var value = 0 var value = 0
let debouncer = Debouncer { let debouncer = Debouncer {
value += 1 value += 1
} }
XCTAssertEqual(0, value) #expect(0 == value)
debouncer.fireNow() debouncer.fireNow()
XCTAssertEqual(value, 0, "The action is performed only when scheduled.") #expect(value == 0, "The action is performed only when scheduled.")
debouncer.schedule() debouncer.schedule()
XCTAssertEqual(value, 0) #expect(value == 0)
debouncer.fireNow() debouncer.fireNow()
XCTAssertEqual(value, 1, "The scheduled action must be performed immediately.") #expect(value == 1, "The scheduled action must be performed immediately.")
} }
func testCancellation() { @Test func cancel() async {
let expectation = self.expectation(description: "Debouncer cancelled") await confirmation("Debouncer cancelled", expectedCount: 0) { confirm in
expectation.isInverted = true let debouncer = Debouncer {
confirm()
let debouncer = Debouncer { }
expectation.fulfill()
debouncer.schedule()
debouncer.cancel()
} }
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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,13 +23,14 @@
// limitations under the License. // limitations under the License.
// //
import AppKit
import Combine import Combine
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class EditedRangeSetTests: XCTestCase { struct EditedRangeSetTests {
func testRangeSet() throws { @Test func rangeSet() throws {
// abcdefg // abcdefg
var set = EditedRangeSet() var set = EditedRangeSet()
@ -37,35 +38,35 @@ final class EditedRangeSetTests: XCTestCase {
// ab|0000|efg // ab|0000|efg
// .replaceCharacters(in: NSRange(2..<3), with: "0000") // .replaceCharacters(in: NSRange(2..<3), with: "0000")
set.append(editedRange: NSRange(location: 2, length: 4), changeInLength: 2) 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 // ab0000e|g
// .replaceCharacters(in: NSRange(7..<8), with: "") // .replaceCharacters(in: NSRange(7..<8), with: "")
set.append(editedRange: NSRange(location: 7, length: 0), changeInLength: -1) 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)]) NSRange(location: 7, length: 0)])
// ab0|0eg // ab0|0eg
// .replaceCharacters(in: NSRange(3..<5), with: "") // .replaceCharacters(in: NSRange(3..<5), with: "")
set.append(editedRange: NSRange(location: 3, length: 0), changeInLength: -2) 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)]) NSRange(location: 5, length: 0)])
// a|1|b00eg // a|1|b00eg
// .replaceCharacters(in: NSRange(1..<1), with: "1") // .replaceCharacters(in: NSRange(1..<1), with: "1")
set.append(editedRange: NSRange(location: 1, length: 1), changeInLength: 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: 3, length: 2),
NSRange(location: 6, length: 0)]) NSRange(location: 6, length: 0)])
set.clear() 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") let textStorage = NSTextStorage("abcdefghij")
var set = EditedRangeSet() var set = EditedRangeSet()
@ -74,22 +75,22 @@ final class EditedRangeSetTests: XCTestCase {
set.append(editedRange: NSRange(location: 2, length: 2), changeInLength: 0) set.append(editedRange: NSRange(location: 2, length: 2), changeInLength: 0)
textStorage.replaceCharacters(in: NSRange(location: 6, length: 2), with: "00") textStorage.replaceCharacters(in: NSRange(location: 6, length: 2), with: "00")
set.append(editedRange: NSRange(location: 6, length: 2), changeInLength: 0) set.append(editedRange: NSRange(location: 6, length: 2), changeInLength: 0)
XCTAssertEqual(textStorage.string, "ab00ef00ij") #expect(textStorage.string == "ab00ef00ij")
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 2), NSRange(location: 6, length: 2)]) #expect(set.ranges == [NSRange(location: 2, length: 2), NSRange(location: 6, length: 2)])
textStorage.replaceCharacters(in: NSRange(location: 3, length: 4), with: "11") textStorage.replaceCharacters(in: NSRange(location: 3, length: 4), with: "11")
set.append(editedRange: NSRange(location: 3, length: 2), changeInLength: -2) set.append(editedRange: NSRange(location: 3, length: 2), changeInLength: -2)
XCTAssertEqual(textStorage.string, "ab0110ij") #expect(textStorage.string == "ab0110ij")
XCTAssertEqual(set.ranges, [NSRange(location: 2, length: 4)]) #expect(set.ranges == [NSRange(location: 2, length: 4)])
textStorage.replaceCharacters(in: NSRange(location: 1, length: 3), with: "22") textStorage.replaceCharacters(in: NSRange(location: 1, length: 3), with: "22")
set.append(editedRange: NSRange(location: 1, length: 2), changeInLength: -1) set.append(editedRange: NSRange(location: 1, length: 2), changeInLength: -1)
XCTAssertEqual(textStorage.string, "a2210ij") #expect(textStorage.string == "a2210ij")
XCTAssertEqual(set.ranges, [NSRange(location: 1, length: 4)]) #expect(set.ranges == [NSRange(location: 1, length: 4)])
} }
func testJoin() throws { @Test func join() throws {
var set = EditedRangeSet() var set = EditedRangeSet()
@ -98,38 +99,35 @@ final class EditedRangeSetTests: XCTestCase {
set.append(editedRange: NSRange(location: 0, length: 2), changeInLength: 0) set.append(editedRange: NSRange(location: 0, length: 2), changeInLength: 0)
set.append(editedRange: NSRange(location: 2, 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") let textStorage = NSTextStorage("abcdefg")
var set = EditedRangeSet() var set = EditedRangeSet()
let expectation = self.expectation(description: "UserDefaults observation for normal key") await confirmation("UserDefaults observation for normal key", expectedCount: 4) { confirm in
expectation.expectedFulfillmentCount = 4 let observer = NotificationCenter.default.publisher(for: NSTextStorage.didProcessEditingNotification, object: textStorage)
.map { $0.object as! NSTextStorage }
let observer = NotificationCenter.default.publisher(for: NSTextStorage.didProcessEditingNotification, object: textStorage) .filter { $0.editedMask.contains(.editedCharacters) }
.map { $0.object as! NSTextStorage } .sink { storage in
.filter { $0.editedMask.contains(.editedCharacters) } set.append(editedRange: storage.editedRange, changeInLength: storage.changeInLength)
.sink { storage in confirm()
set.append(editedRange: storage.editedRange, changeInLength: storage.changeInLength) }
expectation.fulfill()
} textStorage.replaceCharacters(in: NSRange(2..<4), with: "0000")
textStorage.replaceCharacters(in: NSRange(7..<8), with: "")
textStorage.replaceCharacters(in: NSRange(2..<4), with: "0000") textStorage.replaceCharacters(in: NSRange(3..<5), with: "")
textStorage.replaceCharacters(in: NSRange(7..<8), with: "") textStorage.replaceCharacters(in: NSRange(1..<1), with: "1")
textStorage.replaceCharacters(in: NSRange(3..<5), with: "")
textStorage.replaceCharacters(in: NSRange(1..<1), with: "1") #expect(textStorage.string == "a1b00eg")
#expect(set.ranges == [NSRange(location: 1, length: 1),
await self.fulfillment(of: [expectation], timeout: 2) NSRange(location: 3, length: 2),
NSRange(location: 6, length: 0)])
XCTAssertEqual(textStorage.string, "a1b00eg")
XCTAssertEqual(set.ranges, [NSRange(location: 1, length: 1), observer.cancel()
NSRange(location: 3, length: 2), }
NSRange(location: 6, length: 0)])
observer.cancel()
} }
} }

View File

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

View File

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

View File

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

View File

@ -24,34 +24,34 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class FilePermissionTests: XCTestCase { struct FilePermissionTests {
func testFilePermissions() { @Test func filePermissions() {
XCTAssertEqual(FilePermissions(mask: 0o777).mask, 0o777) #expect(FilePermissions(mask: 0o777).mask == 0o777)
XCTAssertEqual(FilePermissions(mask: 0o643).mask, 0o643) #expect(FilePermissions(mask: 0o643).mask == 0o643)
XCTAssertEqual(FilePermissions(mask: 0o777).symbolic, "rwxrwxrwx") #expect(FilePermissions(mask: 0o777).symbolic == "rwxrwxrwx")
XCTAssertEqual(FilePermissions(mask: 0o643).symbolic, "rw-r---wx") #expect(FilePermissions(mask: 0o643).symbolic == "rw-r---wx")
} }
func testFormatStyle() { @Test func formatStyle() {
XCTAssertEqual(FilePermissions(mask: 0o777).formatted(.filePermissions(.full)), "777 (-rwxrwxrwx)") #expect(FilePermissions(mask: 0o777).formatted(.filePermissions(.full)) == "777 (-rwxrwxrwx)")
XCTAssertEqual(FilePermissions(mask: 0o643).formatted(.filePermissions(.full)), "643 (-rw-r---wx)") #expect(FilePermissions(mask: 0o643).formatted(.filePermissions(.full)) == "643 (-rw-r---wx)")
} }
func testCalculation() { @Test func calculate() {
var permissions = FilePermissions(mask: 0o644) var permissions = FilePermissions(mask: 0o644)
permissions.user.insert(.execute) permissions.user.insert(.execute)
XCTAssertTrue(permissions.user.contains(.execute)) #expect(permissions.user.contains(.execute))
XCTAssertEqual(permissions.mask, 0o744) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,40 +24,41 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit.NSFont
import Testing
@testable import CotEditor @testable import CotEditor
final class FontExtensionTests: XCTestCase { struct FontExtensionTests {
func testFontSize() { @Test func fontSize() {
let font = NSFont(name: "Menlo-Regular", size: 11) 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 regularFont = try #require(NSFont(name: "Menlo-Regular", size: 11))
let boldFont = try XCTUnwrap(NSFont(name: "Menlo-Bold", size: 11)) let boldFont = try #require(NSFont(name: "Menlo-Bold", size: 11))
XCTAssertEqual(regularFont.weight, .regular) #expect(regularFont.weight == .regular)
XCTAssertEqual(boldFont.weight.rawValue, NSFont.Weight.bold.rawValue, accuracy: 0.00001) // #expect(boldFont.weight.rawValue == NSFont.Weight.bold.rawValue) // accuracy: 0.00001
// The const value is (unfortunately) not exact equal... // The const value is (unfortunately) not exact equal...
XCTAssertEqual(boldFont.weight.rawValue, 0.4) #expect(boldFont.weight.rawValue == 0.4)
XCTAssertNotEqual(NSFont.Weight.bold.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)) let menlo = try #require(NSFont(named: .menlo, size: 11))
XCTAssertEqual(menlo, NSFont(name: "Menlo-Regular", size: 11)) #expect(menlo == NSFont(name: "Menlo-Regular", size: 11))
let avenirNextCondensed = try XCTUnwrap(NSFont(named: .avenirNextCondensed, weight: .bold, size: 11)) let avenirNextCondensed = try #require(NSFont(named: .avenirNextCondensed, weight: .bold, size: 11))
XCTAssertEqual(avenirNextCondensed, NSFont(name: "AvenirNextCondensed-Bold", size: 11)) #expect(avenirNextCondensed == NSFont(name: "AvenirNextCondensed-Bold", size: 11))
XCTAssertEqual(avenirNextCondensed.weight.rawValue, NSFont.Weight.bold.rawValue, accuracy: 0.00001) // #expect(avenirNextCondensed.weight.rawValue == NSFont.Weight.bold.rawValue) // accuracy: 0.00001
} }
} }

View File

@ -23,47 +23,47 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class FormatStylesTests: XCTestCase { struct FormatStylesTests {
func testCSVFormatStyle() { @Test func formatCSV() throws {
XCTAssertEqual(["dog", "cat"].formatted(.csv), "dog, cat") #expect(["dog", "cat"].formatted(.csv) == "dog, cat")
XCTAssertEqual(["dog"].formatted(.csv), "dog") #expect(["dog"].formatted(.csv) == "dog")
XCTAssertEqual(["dog", "", "dog", ""].formatted(.csv), "dog, , dog, ") #expect(["dog", "", "dog", ""].formatted(.csv) == "dog, , dog, ")
XCTAssertEqual(["dog", "", "dog", ""].formatted(.csv(omittingEmptyItems: true)), "dog, dog") #expect(["dog", "", "dog", ""].formatted(.csv(omittingEmptyItems: true)) == "dog, dog")
let strategy = CSVFormatStyle().parseStrategy let strategy = CSVFormatStyle().parseStrategy
XCTAssertEqual(try strategy.parse("dog, cat"), ["dog", "cat"]) #expect(try strategy.parse("dog, cat") == ["dog", "cat"])
XCTAssertEqual(try strategy.parse(" a,b,c"), ["a", "b", "c"]) #expect(try strategy.parse(" a,b,c") == ["a", "b", "c"])
XCTAssertEqual(try strategy.parse(" a, ,c"), ["a", "", "c"]) #expect(try strategy.parse(" a, ,c") == ["a", "", "c"])
XCTAssertEqual(try CSVFormatStyle(omittingEmptyItems: true).parseStrategy.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)) let formatter = RangedIntegerFormatStyle(range: 1...(.max))
XCTAssertEqual(formatter.format(-3), "1") #expect(formatter.format(-3) == "1")
XCTAssertEqual(try formatter.parseStrategy.parse("0"), 1) #expect(try formatter.parseStrategy.parse("0") == 1)
XCTAssertEqual(try formatter.parseStrategy.parse("1"), 1) #expect(try formatter.parseStrategy.parse("1") == 1)
XCTAssertEqual(try formatter.parseStrategy.parse("2"), 2) #expect(try formatter.parseStrategy.parse("2") == 2)
XCTAssertEqual(try formatter.parseStrategy.parse("a"), 1) #expect(try formatter.parseStrategy.parse("a") == 1)
} }
func testRangedIntegerWithDefault() throws { @Test func rangedIntegerWithDefault() throws {
let formatter = RangedIntegerFormatStyle(range: -1...(.max), defaultValue: 4) let formatter = RangedIntegerFormatStyle(range: -1...(.max), defaultValue: 4)
XCTAssertEqual(formatter.format(-3), "-1") #expect(formatter.format(-3) == "-1")
XCTAssertEqual(try formatter.parseStrategy.parse("-2"), -1) #expect(try formatter.parseStrategy.parse("-2") == -1)
XCTAssertEqual(try formatter.parseStrategy.parse("-1"), -1) #expect(try formatter.parseStrategy.parse("-1") == -1)
XCTAssertEqual(try formatter.parseStrategy.parse("0"), 0) #expect(try formatter.parseStrategy.parse("0") == 0)
XCTAssertEqual(try formatter.parseStrategy.parse("2"), 2) #expect(try formatter.parseStrategy.parse("2") == 2)
XCTAssertEqual(try formatter.parseStrategy.parse("a"), 4) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,14 +24,15 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class FourCharCodeTests: XCTestCase { struct FourCharCodeTests {
func testInitializer() { @Test func initialize() {
XCTAssertEqual(FourCharCode(stringLiteral: "TEXT"), NSHFSTypeCodeFromFileType("'TEXT'")) #expect(FourCharCode(stringLiteral: "TEXT") == NSHFSTypeCodeFromFileType("'TEXT'"))
XCTAssertEqual("rtfd", NSHFSTypeCodeFromFileType("'rtfd'")) #expect("rtfd" == NSHFSTypeCodeFromFileType("'rtfd'"))
} }
} }

View File

@ -23,114 +23,116 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class FuzzyRangeTests: XCTestCase { struct FuzzyRangeTests {
func testFuzzyCharacterRange() { @Test func fuzzyCharacterRange() {
let string = "0123456789" let string = "0123456789"
XCTAssertEqual(string.range(in: FuzzyRange(location: 2, length: 2)), NSRange(location: 2, length: 2)) #expect(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)) #expect(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)) #expect(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)) #expect(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: 3, length: -2)) == NSRange(location: 3, length: "45678".utf16.count))
// grapheme cluster 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 let string = "1\r\n2\r\n3\r\n4" // 1 based
var range: NSRange var range: NSRange
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 1, length: 2))) range = try #require(string.rangeForLine(in: FuzzyRange(location: 1, length: 2)))
XCTAssertEqual((string as NSString).substring(with: range), "1\r\n2\r\n") #expect((string as NSString).substring(with: range) == "1\r\n2\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 4, length: 1))) range = try #require(string.rangeForLine(in: FuzzyRange(location: 4, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "4") #expect((string as NSString).substring(with: range) == "4")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 3, length: 0))) range = try #require(string.rangeForLine(in: FuzzyRange(location: 3, length: 0)))
XCTAssertEqual((string as NSString).substring(with: range), "3\r\n") #expect((string as NSString).substring(with: range) == "3\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: -1, length: 1))) range = try #require(string.rangeForLine(in: FuzzyRange(location: -1, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "4") #expect((string as NSString).substring(with: range) == "4")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: -2, length: 1))) range = try #require(string.rangeForLine(in: FuzzyRange(location: -2, length: 1)))
XCTAssertEqual((string as NSString).substring(with: range), "3\r\n") #expect((string as NSString).substring(with: range) == "3\r\n")
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 2, length: -2))) range = try #require(string.rangeForLine(in: FuzzyRange(location: 2, length: -2)))
XCTAssertEqual((string as NSString).substring(with: range), "2\r\n") #expect((string as NSString).substring(with: range) == "2\r\n")
range = try XCTUnwrap("1\n".rangeForLine(in: FuzzyRange(location: -1, length: 0))) range = try #require("1\n".rangeForLine(in: FuzzyRange(location: -1, length: 0)))
XCTAssertEqual(range, NSRange(location: 2, length: 0)) #expect(range == NSRange(location: 2, length: 0))
range = try XCTUnwrap(string.rangeForLine(in: FuzzyRange(location: 1, length: 2), includingLineEnding: false)) range = try #require(string.rangeForLine(in: FuzzyRange(location: 1, length: 2), includingLineEnding: false))
XCTAssertEqual((string as NSString).substring(with: range), "1\r\n2") #expect((string as NSString).substring(with: range) == "1\r\n2")
} }
func testFormattingFuzzyRange() { @Test func formatFuzzyRange() {
XCTAssertEqual(FuzzyRange(location: 0, length: 0).formatted(), "0") #expect(FuzzyRange(location: 0, length: 0).formatted() == "0")
XCTAssertEqual(FuzzyRange(location: 1, length: 0).formatted(), "1") #expect(FuzzyRange(location: 1, length: 0).formatted() == "1")
XCTAssertEqual(FuzzyRange(location: 1, length: 1).formatted(), "1") #expect(FuzzyRange(location: 1, length: 1).formatted() == "1")
XCTAssertEqual(FuzzyRange(location: 1, length: 2).formatted(), "1:2") #expect(FuzzyRange(location: 1, length: 2).formatted() == "1:2")
XCTAssertEqual(FuzzyRange(location: -1, length: 0).formatted(), "-1") #expect(FuzzyRange(location: -1, length: 0).formatted() == "-1")
XCTAssertEqual(FuzzyRange(location: -1, length: -1).formatted(), "-1:-1") #expect(FuzzyRange(location: -1, length: -1).formatted() == "-1:-1")
} }
func testParsingFuzzyRange() throws { @Test func parseFuzzyRange() throws {
let parser = FuzzyRangeParseStrategy() let parser = FuzzyRangeParseStrategy()
XCTAssertEqual(try parser.parse("0"), FuzzyRange(location: 0, length: 0)) #expect(try parser.parse("0") == FuzzyRange(location: 0, length: 0))
XCTAssertEqual(try parser.parse("1"), FuzzyRange(location: 1, length: 0)) #expect(try parser.parse("1") == FuzzyRange(location: 1, length: 0))
XCTAssertEqual(try parser.parse("1:2"), FuzzyRange(location: 1, length: 2)) #expect(try parser.parse("1:2") == FuzzyRange(location: 1, length: 2))
XCTAssertEqual(try parser.parse("-1"), FuzzyRange(location: -1, length: 0)) #expect(try parser.parse("-1") == FuzzyRange(location: -1, length: 0))
XCTAssertEqual(try parser.parse("-1:-1"), FuzzyRange(location: -1, length: -1)) #expect(try parser.parse("-1:-1") == FuzzyRange(location: -1, length: -1))
XCTAssertThrowsError(try parser.parse(""))
XCTAssertThrowsError(try parser.parse("abc")) #expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("") }
XCTAssertThrowsError(try parser.parse("1:a")) #expect(throws: FuzzyRangeParseStrategy.ParseError.invalidValue) { try parser.parse("abc") }
XCTAssertThrowsError(try parser.parse("1:1:1")) #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 let string = "1\r\n2\r\n3\r\n456\n567" // 1 based
XCTAssertEqual(try string.fuzzyLocation(line: 0), 0) #expect(try string.fuzzyLocation(line: 0) == 0)
XCTAssertEqual(try string.fuzzyLocation(line: 0, column: 1), 1) #expect(try string.fuzzyLocation(line: 0, column: 1) == 1)
XCTAssertEqual(try string.fuzzyLocation(line: 1), 0) #expect(try string.fuzzyLocation(line: 1) == 0)
XCTAssertEqual(try string.fuzzyLocation(line: 2), 3) #expect(try string.fuzzyLocation(line: 2) == 3)
XCTAssertEqual(try string.fuzzyLocation(line: 4), 9) #expect(try string.fuzzyLocation(line: 4) == 9)
XCTAssertEqual(try string.fuzzyLocation(line: 5), 13) #expect(try string.fuzzyLocation(line: 5) == 13)
XCTAssertEqual(try string.fuzzyLocation(line: -1), 13) #expect(try string.fuzzyLocation(line: -1) == 13)
XCTAssertEqual(try string.fuzzyLocation(line: -2), 9) #expect(try string.fuzzyLocation(line: -2) == 9)
XCTAssertEqual(try string.fuzzyLocation(line: -5), 0) #expect(try string.fuzzyLocation(line: -5) == 0)
XCTAssertThrowsError(try string.fuzzyLocation(line: -6)) #expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: -6) }
// line with a line ending // line with a line ending
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 0), 9) #expect(try string.fuzzyLocation(line: 4, column: 0) == 9)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 1), 10) #expect(try string.fuzzyLocation(line: 4, column: 1) == 10)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: 3), 12) #expect(try string.fuzzyLocation(line: 4, column: 3) == 12)
XCTAssertThrowsError(try string.fuzzyLocation(line: 4, column: 4)) #expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: 4, column: 4) }
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: -1), 12) #expect(try string.fuzzyLocation(line: 4, column: -1) == 12)
XCTAssertEqual(try string.fuzzyLocation(line: 4, column: -2), 11) #expect(try string.fuzzyLocation(line: 4, column: -2) == 11)
// line without any line endings (the last line) // line without any line endings (the last line)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 0), 13) #expect(try string.fuzzyLocation(line: 5, column: 0) == 13)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 1), 14) #expect(try string.fuzzyLocation(line: 5, column: 1) == 14)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: 3), 16) #expect(try string.fuzzyLocation(line: 5, column: 3) == 16)
XCTAssertThrowsError(try string.fuzzyLocation(line: 5, column: 4)) #expect(throws: FuzzyLocationError.self) { try string.fuzzyLocation(line: 5, column: 4) }
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: -1), 16) #expect(try string.fuzzyLocation(line: 5, column: -1) == 16)
XCTAssertEqual(try string.fuzzyLocation(line: 5, column: -2), 15) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,37 +24,38 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import CoreGraphics
import Testing
@testable import CotEditor @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)) #expect(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)) #expect(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(CGRect(x: 2, y: 1, width: 2, height: 1).scaled(to: 2) ==
CGRect(x: 4, y: 2, width: 4, height: 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)) #expect(-CGPoint(x: 2, y: 3) == CGPoint(x: -2, y: -3))
XCTAssertEqual(-CGSize(width: 2, height: 3), CGSize(width: -2, height: -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)) #expect(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) #expect(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).offset(by: -CGSize(width: 2, height: 3)) == .zero)
} }
} }

View File

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

View File

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

View File

@ -24,28 +24,29 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class LineEndingTests: XCTestCase { struct LineEndingTests {
func testLineEnding() { @Test func lineEnding() {
XCTAssertEqual(LineEnding.lf.rawValue, "\n") #expect(LineEnding.lf.rawValue == "\n")
XCTAssertEqual(LineEnding.crlf.rawValue, "\r\n") #expect(LineEnding.crlf.rawValue == "\r\n")
XCTAssertEqual(LineEnding.paragraphSeparator.rawValue, "\u{2029}") #expect(LineEnding.paragraphSeparator.rawValue == "\u{2029}")
} }
func testName() { @Test func name() {
XCTAssertEqual(LineEnding.lf.label, "LF") #expect(LineEnding.lf.label == "LF")
XCTAssertEqual(LineEnding.crlf.label, "CRLF") #expect(LineEnding.crlf.label == "CRLF")
XCTAssertEqual(LineEnding.paragraphSeparator.label, "PS") #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 string = "\rfoo\r\nbar \n \nb \n\r uz\u{2029}moin\r\n"
let expected: [ValueRange<LineEnding>] = [ let expected: [ValueRange<LineEnding>] = [
@ -59,16 +60,16 @@ final class LineEndingTests: XCTestCase {
.init(value: .crlf, location: 25), .init(value: .crlf, location: 25),
] ]
XCTAssert("".lineEndingRanges().isEmpty) #expect("".lineEndingRanges().isEmpty)
XCTAssert("abc".lineEndingRanges().isEmpty) #expect("abc".lineEndingRanges().isEmpty)
XCTAssertEqual(string.lineEndingRanges(), expected) #expect(string.lineEndingRanges() == expected)
} }
func testReplacement() { @Test func replace() {
XCTAssertEqual("foo\r\nbar\n".replacingLineEndings(with: .cr), "foo\rbar\r") #expect("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\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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,29 +23,30 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class LineRangeCacheableTests: XCTestCase { struct LineRangeCacheableTests {
private let repeatCount = 20 private let repeatCount = 20
func testLineNumberCalculation() { @Test func calculateLineNumber() {
let lineString = LineString("dog \n\n cat \n cow \n") let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineNumber(at: 0), 1) #expect(lineString.lineNumber(at: 0) == 1)
XCTAssertEqual(lineString.lineNumber(at: 1), 1) #expect(lineString.lineNumber(at: 1) == 1)
XCTAssertEqual(lineString.lineNumber(at: 4), 1) #expect(lineString.lineNumber(at: 4) == 1)
XCTAssertEqual(lineString.lineNumber(at: 5), 2) #expect(lineString.lineNumber(at: 5) == 2)
XCTAssertEqual(lineString.lineNumber(at: 6), 3) #expect(lineString.lineNumber(at: 6) == 3)
XCTAssertEqual(lineString.lineNumber(at: 11), 3) #expect(lineString.lineNumber(at: 11) == 3)
XCTAssertEqual(lineString.lineNumber(at: 12), 4) #expect(lineString.lineNumber(at: 12) == 4)
XCTAssertEqual(lineString.lineNumber(at: 17), 4) #expect(lineString.lineNumber(at: 17) == 4)
XCTAssertEqual(lineString.lineNumber(at: 18), 5) #expect(lineString.lineNumber(at: 18) == 5)
let lineString2 = LineString("dog \n\n cat \n cow ") 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 { for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled()) let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
@ -53,27 +54,28 @@ final class LineRangeCacheableTests: XCTestCase {
for index in (0...string.length).shuffled() { for index in (0...string.length).shuffled() {
let result = (string as NSString).lineNumber(at: index) 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") let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineRange(at: 0), NSRange(0..<5)) #expect(lineString.lineRange(at: 0) == NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<5)) #expect(lineString.lineRange(at: 1) == NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(0..<5)) #expect(lineString.lineRange(at: 4) == NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(at: 5), NSRange(5..<6)) #expect(lineString.lineRange(at: 5) == NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(at: 6), NSRange(6..<12)) #expect(lineString.lineRange(at: 6) == NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(at: 11), NSRange(6..<12)) #expect(lineString.lineRange(at: 11) == NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(at: 12), NSRange(12..<18)) #expect(lineString.lineRange(at: 12) == NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(at: 17), NSRange(12..<18)) #expect(lineString.lineRange(at: 17) == NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(at: 18), NSRange(18..<18)) #expect(lineString.lineRange(at: 18) == NSRange(18..<18))
let lineString2 = LineString("dog \n\n cat \n cow ") 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 { for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled()) let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
@ -81,66 +83,69 @@ final class LineRangeCacheableTests: XCTestCase {
for index in (0...string.length).shuffled() { for index in (0...string.length).shuffled() {
let result = (string as NSString).lineRange(at: index) let result = (string as NSString).lineRange(at: index)
XCTAssertEqual(lineString.lineRange(at: index), result, "At \(index) with string \"\(string)\"") #expect(lineString.lineRange(at: index) == result,
XCTAssertEqual(lineString.lineStartIndex(at: index), result.lowerBound, "At \(index) with string \"\(string)\"") "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") let lineString = LineString("dog \n\n cat \n cow")
XCTAssertEqual(lineString.lineContentRange(for: NSRange(0..<3)), NSRange(0..<4)) #expect(lineString.lineContentRange(for: NSRange(0..<3)) == NSRange(0..<4))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(4..<6)), NSRange(0..<6)) #expect(lineString.lineContentRange(for: NSRange(4..<6)) == NSRange(0..<6))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(5..<6)), NSRange(5..<6)) #expect(lineString.lineContentRange(for: NSRange(5..<6)) == NSRange(5..<6))
XCTAssertEqual(lineString.lineContentRange(for: NSRange(7..<13)), NSRange(6..<16)) #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") let lineString = LineString("dog \n\n cat \n cow \n")
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<3)), NSRange(0..<5)) #expect(lineString.lineRange(for: NSRange(0..<3)) == NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<5)), NSRange(0..<5)) #expect(lineString.lineRange(for: NSRange(0..<5)) == NSRange(0..<5))
XCTAssertEqual(lineString.lineRange(for: NSRange(0..<6)), NSRange(0..<6)) #expect(lineString.lineRange(for: NSRange(0..<6)) == NSRange(0..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<5)), NSRange(5..<6)) #expect(lineString.lineRange(for: NSRange(5..<5)) == NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<6)), NSRange(5..<6)) #expect(lineString.lineRange(for: NSRange(5..<6)) == NSRange(5..<6))
XCTAssertEqual(lineString.lineRange(for: NSRange(5..<7)), NSRange(5..<12)) #expect(lineString.lineRange(for: NSRange(5..<7)) == NSRange(5..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<6)), NSRange(6..<12)) #expect(lineString.lineRange(for: NSRange(6..<6)) == NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<7)), NSRange(6..<12)) #expect(lineString.lineRange(for: NSRange(6..<7)) == NSRange(6..<12))
XCTAssertEqual(lineString.lineRange(for: NSRange(6..<17)), NSRange(6..<18)) #expect(lineString.lineRange(for: NSRange(6..<17)) == NSRange(6..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(17..<17)), NSRange(12..<18)) #expect(lineString.lineRange(for: NSRange(17..<17)) == NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(17..<18)), NSRange(12..<18)) #expect(lineString.lineRange(for: NSRange(17..<18)) == NSRange(12..<18))
XCTAssertEqual(lineString.lineRange(for: NSRange(18..<18)), NSRange(18..<18)) #expect(lineString.lineRange(for: NSRange(18..<18)) == NSRange(18..<18))
let lineString2 = LineString("dog \n\n cat \n cow ") let lineString2 = LineString("dog \n\n cat \n cow ")
XCTAssertEqual(lineString2.lineRange(for: NSRange(15..<17)), NSRange(12..<17)) #expect(lineString2.lineRange(for: NSRange(15..<17)) == NSRange(12..<17))
XCTAssertEqual(lineString2.lineRange(for: NSRange(17..<17)), NSRange(12..<17)) #expect(lineString2.lineRange(for: NSRange(17..<17)) == NSRange(12..<17))
for _ in 0..<self.repeatCount { for _ in 0..<self.repeatCount {
let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled()) let string = String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())
let lineString = LineString(string) let lineString = LineString(string)
for index in (0...string.length).shuffled() { 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 range = NSRange(index..<endIndex)
let result = (string as NSString).lineRange(for: range) 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 lineString = LineString("\n🐶")
let lineNumber = lineString.lineNumber(at: 1) let lineNumber = lineString.lineNumber(at: 1)
let lineRange = lineString.lineRange(at: 1) let lineRange = lineString.lineRange(at: 1)
lineString.invalidateLineRanges(in: NSRange(1..<2), changeInLength: 0) lineString.invalidateLineRanges(in: NSRange(1..<2), changeInLength: 0)
XCTAssertEqual(lineString.lineNumber(at: 1), lineNumber) // 2 #expect(lineString.lineNumber(at: 1) == lineNumber) // 2
XCTAssertEqual(lineString.lineRange(at: 1), lineRange) // NSRange(1..<3) #expect(lineString.lineRange(at: 1) == lineRange) // NSRange(1..<3)
for _ in 0..<self.repeatCount { for _ in 0..<self.repeatCount {
let lineString = LineString(String(" 🐶 \n 🐱 \n 🐮 \n".shuffled())) let lineString = LineString(String(" 🐶 \n 🐱 \n 🐮 \n".shuffled()))
@ -152,58 +157,61 @@ final class LineRangeCacheableTests: XCTestCase {
lineString.invalidateLineRanges(in: range, changeInLength: 0) lineString.invalidateLineRanges(in: range, changeInLength: 0)
XCTAssertEqual(lineString.lineNumber(at: index), lineNumber, "At \(index) with string \"\(lineString.string)\"") #expect(lineString.lineNumber(at: index) == lineNumber,
XCTAssertEqual(lineString.lineRange(at: index), lineRange, "At \(index) with string \"\(lineString.string)\"") "At \(index) with string \"\(lineString.string)\"")
XCTAssertEqual(lineString.lineStartIndex(at: index), lineRange.lowerBound, "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 ") let lineString = LineString("dog \n\n\n cat \n ")
_ = lineString.lineNumber(at: lineString.string.length) _ = lineString.lineNumber(at: lineString.string.length)
lineString.replaceCharacters(in: NSRange(1..<3), with: "") // "og" lineString.replaceCharacters(in: NSRange(1..<3), with: "") // "og"
XCTAssertEqual(lineString.string, "d \n\n\n cat \n ") #expect(lineString.string == "d \n\n\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1) #expect(lineString.lineNumber(at: 1) == 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<3)) // "d \n" #expect(lineString.lineRange(at: 1) == NSRange(0..<3)) // "d \n"
XCTAssertEqual(lineString.lineNumber(at: 3), 2) #expect(lineString.lineNumber(at: 3) == 2)
XCTAssertEqual(lineString.lineRange(at: 3), NSRange(3..<4)) // "\n" #expect(lineString.lineRange(at: 3) == NSRange(3..<4)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(4..<5)) // "\n" #expect(lineString.lineRange(at: 4) == NSRange(4..<5)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 5), NSRange(5..<11)) // " cat \n" #expect(lineString.lineRange(at: 5) == NSRange(5..<11)) // " cat \n"
lineString.replaceCharacters(in: NSRange(1..<2), with: "") // 1st " " lineString.replaceCharacters(in: NSRange(1..<2), with: "") // 1st " "
XCTAssertEqual(lineString.string, "d\n\n\n cat \n ") #expect(lineString.string == "d\n\n\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1) #expect(lineString.lineNumber(at: 1) == 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<2)) // "d\n" #expect(lineString.lineRange(at: 1) == NSRange(0..<2)) // "d\n"
XCTAssertEqual(lineString.lineRange(at: 2), NSRange(2..<3)) // "\n" #expect(lineString.lineRange(at: 2) == NSRange(2..<3)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 3), NSRange(3..<4)) // "\n" #expect(lineString.lineRange(at: 3) == NSRange(3..<4)) // "\n"
XCTAssertEqual(lineString.lineRange(at: 4), NSRange(4..<10)) // " cat \n" #expect(lineString.lineRange(at: 4) == NSRange(4..<10)) // " cat \n"
lineString.replaceCharacters(in: NSRange(2..<4), with: "") // "\n\n" lineString.replaceCharacters(in: NSRange(2..<4), with: "") // "\n\n"
XCTAssertEqual(lineString.string, "d\n cat \n ") #expect(lineString.string == "d\n cat \n ")
XCTAssertEqual(lineString.lineNumber(at: 1), 1) #expect(lineString.lineNumber(at: 1) == 1)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(0..<2)) // "d\n" #expect(lineString.lineRange(at: 1) == NSRange(0..<2)) // "d\n"
XCTAssertEqual(lineString.lineRange(at: 2), NSRange(2..<8)) // " cat \n" #expect(lineString.lineRange(at: 2) == NSRange(2..<8)) // " cat \n"
} }
func testStringModification() { @Test func modifyString() {
let lineString = LineString("\n🐶") let lineString = LineString("\n🐶")
_ = lineString.lineNumber(at: 1) _ = lineString.lineNumber(at: 1)
lineString.replaceCharacters(in: NSRange(1..<3), with: "a\nb") lineString.replaceCharacters(in: NSRange(1..<3), with: "a\nb")
lineString.invalidateLineRanges(in: NSRange(1..<3), changeInLength: 1) lineString.invalidateLineRanges(in: NSRange(1..<3), changeInLength: 1)
XCTAssertEqual(lineString.lineNumber(at: 1), 2) #expect(lineString.lineNumber(at: 1) == 2)
XCTAssertEqual(lineString.lineRange(at: 1), NSRange(1..<3)) // "a\n" #expect(lineString.lineRange(at: 1) == NSRange(1..<3)) // "a\n"
for _ in 0..<self.repeatCount { for _ in 0..<self.repeatCount {
let string = String(" dog \n cat \n cow \n".shuffled()) let string = String(" dog \n cat \n cow \n".shuffled())
let lineString = LineString(string) 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 location = Int.random(in: 0..<(string.length - 1))
let length = Int.random(in: 0..<(string.length - location)) let length = Int.random(in: 0..<(string.length - location))
@ -213,30 +221,29 @@ final class LineRangeCacheableTests: XCTestCase {
lineString.replaceCharacters(in: range, with: replacement) lineString.replaceCharacters(in: range, with: replacement)
for index in (0...lineString.string.length).shuffled() { 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)\"") "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)\"") "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)\"") "at \(index) with string \"\(lineString.string)\"")
} }
} }
} }
func testEdgeModification() { @Test func modifyEdge() {
let lineString = LineString("\n \n") let lineString = LineString("\n \n")
XCTAssertEqual(lineString.lineNumber(at: 4), 3) #expect(lineString.lineNumber(at: 4) == 3)
lineString.replaceCharacters(in: NSRange(0..<0), with: " ") lineString.replaceCharacters(in: NSRange(0..<0), with: " ")
let index = 4 #expect(lineString.string == " \n \n")
XCTAssertEqual(lineString.string, " \n \n") #expect(lineString.lineNumber(at: 4) == 2)
XCTAssertEqual(lineString.lineNumber(at: index), 2) #expect(lineString.lineRange(at: 4) == NSRange(location: 3, length: 3))
XCTAssertEqual(lineString.lineRange(at: index), NSRange(location: 3, length: 3)) #expect(lineString.lineStartIndex(at: 4) == 3)
XCTAssertEqual(lineString.lineStartIndex(at: index), 3)
} }
} }

View File

@ -24,10 +24,11 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class LineSortTests: XCTestCase { struct LineSortTests {
private let lines = """ private let lines = """
dog, 🐕, 2, dog, 🐕, 2,
@ -36,7 +37,7 @@ final class LineSortTests: XCTestCase {
""" """
func testCSVSort() { @Test func csvSort() {
var pattern = CSVSortPattern() var pattern = CSVSortPattern()
pattern.column = 3 pattern.column = 3
@ -47,14 +48,14 @@ final class LineSortTests: XCTestCase {
cow, 🐄, 3, cow, 🐄, 3,
""" """
XCTAssertEqual(pattern.sort(self.lines), result) #expect(pattern.sort(self.lines) == result)
XCTAssertEqual(pattern.sort(""), "") #expect(pattern.sort("").isEmpty)
XCTAssertNoThrow(try pattern.validate()) #expect(throws: Never.self) { try pattern.validate() }
XCTAssertNil(pattern.range(for: "dog, 🐕, , イヌ")) #expect(pattern.range(for: "dog, 🐕, , イヌ") == nil)
} }
func testRegexSort() throws { @Test func regexSort() throws {
var pattern = RegularExpressionSortPattern() var pattern = RegularExpressionSortPattern()
pattern.searchPattern = ", ([0-9])," pattern.searchPattern = ", ([0-9]),"
@ -65,24 +66,24 @@ final class LineSortTests: XCTestCase {
cow, 🐄, 3, cow, 🐄, 3,
""" """
XCTAssertEqual(pattern.sort(self.lines), result) #expect(pattern.sort(self.lines) == result)
pattern.usesCaptureGroup = true pattern.usesCaptureGroup = true
pattern.group = 1 pattern.group = 1
XCTAssertEqual(pattern.sort(self.lines), result) #expect(pattern.sort(self.lines) == result)
XCTAssertEqual(pattern.sort(""), "") #expect(pattern.sort("").isEmpty)
XCTAssertNoThrow(try pattern.validate()) #expect(throws: Never.self) { try pattern.validate() }
pattern.searchPattern = "\\" pattern.searchPattern = "\\"
XCTAssertThrowsError(try pattern.validate()) #expect(throws: SortPatternError.invalidRegularExpressionPattern) { try pattern.validate() }
pattern.searchPattern = "(a)(b)c" pattern.searchPattern = "(a)(b)c"
try pattern.validate() try pattern.validate()
XCTAssertEqual(pattern.numberOfCaptureGroups, 2) #expect(pattern.numberOfCaptureGroups == 2)
} }
func testFuzzySort() { @Test func fuzzySort() {
var pattern = CSVSortPattern() var pattern = CSVSortPattern()
pattern.column = 4 pattern.column = 4
@ -96,13 +97,13 @@ final class LineSortTests: XCTestCase {
cat, 🐈, 1, cat, 🐈, 1,
""" """
XCTAssertEqual(pattern.sort(self.lines, options: options), result) #expect(pattern.sort(self.lines, options: options) == result)
XCTAssertEqual(pattern.sort(""), "") #expect(pattern.sort("").isEmpty)
XCTAssertNoThrow(try pattern.validate()) #expect(throws: Never.self) { try pattern.validate() }
} }
func testNumericSorts() { @Test func numericSort() {
let pattern = EntireLineSortPattern() let pattern = EntireLineSortPattern()
let numbers = """ let numbers = """
@ -114,71 +115,71 @@ final class LineSortTests: XCTestCase {
var options = SortOptions() var options = SortOptions()
options.numeric = false options.numeric = false
XCTAssertEqual(pattern.sort(numbers, options: options), "1\n12\n3") #expect(pattern.sort(numbers, options: options) == "1\n12\n3")
options.numeric = true options.numeric = true
XCTAssertEqual(pattern.sort(numbers, options: options), "1\n3\n12") #expect(pattern.sort(numbers, options: options) == "1\n3\n12")
options.descending = true 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.descending = false
options.keepsFirstLine = true 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" let string = "dog"
XCTAssertEqual(EntireLineSortPattern().range(for: string), string.startIndex..<string.endIndex) #expect(EntireLineSortPattern().range(for: string) == string.startIndex..<string.endIndex)
XCTAssertEqual(CSVSortPattern().range(for: string), string.startIndex..<string.endIndex) #expect(CSVSortPattern().range(for: string) == string.startIndex..<string.endIndex)
XCTAssertNil(RegularExpressionSortPattern().range(for: string)) #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 " let csvString = " dog , dog cow "
var pattern = CSVSortPattern() var pattern = CSVSortPattern()
pattern.column = 2 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" let tsvString = "a\tb"
pattern.column = 1 pattern.column = 1
let range = try XCTUnwrap(pattern.range(for: tsvString)) let range = try #require(pattern.range(for: tsvString))
XCTAssertEqual(pattern.sortKey(for: tsvString), tsvString) #expect(pattern.sortKey(for: tsvString) == tsvString)
XCTAssertEqual(NSRange(range, in: tsvString), NSRange(0..<3)) #expect(NSRange(range, in: tsvString) == NSRange(0..<3))
} }
func testNumberParse() throws { @Test func parseNumber() throws {
var options = SortOptions() var options = SortOptions()
options.locale = .init(identifier: "en") options.locale = .init(identifier: "en")
XCTAssertTrue(options.isLocalized) #expect(options.isLocalized)
XCTAssertTrue(options.numeric) #expect(options.numeric)
XCTAssertEqual(options.parse("0"), 0) #expect(options.parse("0") == 0)
XCTAssertEqual(options.parse("10 000"), 10000) #expect(options.parse("10 000") == 10000)
XCTAssertEqual(options.parse("-1000.1 m/s"), -1000.1) #expect(options.parse("-1000.1 m/s") == -1000.1)
XCTAssertEqual(options.parse("-1000,1 m/s"), -1000) #expect(options.parse("-1000,1 m/s") == -1000)
XCTAssertEqual(options.parse("+1,000"), 1000) #expect(options.parse("+1,000") == 1000)
XCTAssertNil(options.parse("dog 10")) #expect(options.parse("dog 10") == nil)
options.locale = .init(identifier: "de") options.locale = .init(identifier: "de")
XCTAssertTrue(options.numeric) #expect(options.numeric)
XCTAssertEqual(options.parse("0"), 0) #expect(options.parse("0") == 0)
XCTAssertEqual(options.parse("10 000"), 10000) #expect(options.parse("10 000") == 10000)
XCTAssertEqual(options.parse("-1000.1 m/s"), -1000) #expect(options.parse("-1000.1 m/s") == -1000)
XCTAssertEqual(options.parse("-1000,1 m/s"), -1000.1) #expect(options.parse("-1000,1 m/s") == -1000.1)
XCTAssertEqual(options.parse("+1,000"), 1) #expect(options.parse("+1,000") == 1)
XCTAssertNil(options.parse("dog 10")) #expect(options.parse("dog 10") == nil)
options.numeric = false options.numeric = false
XCTAssertNil(options.parse("0")) #expect(options.parse("0") == nil)
XCTAssertNil(options.parse("10 000")) #expect(options.parse("10 000") == nil)
XCTAssertNil(options.parse("-1000.1 m/s")) #expect(options.parse("-1000.1 m/s") == nil)
XCTAssertNil(options.parse("-1000,1 m/s")) #expect(options.parse("-1000,1 m/s") == nil)
XCTAssertNil(options.parse("+1,000")) #expect(options.parse("+1,000") == nil)
XCTAssertNil(options.parse("dog 10")) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,69 +23,70 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @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 foo = NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"])
let bar = NSAttributedString(string: "bar", attributes: [:]) let bar = NSAttributedString(string: "bar", attributes: [:])
let fooBar = foo + bar let fooBar = foo + bar
XCTAssertFalse(fooBar is NSMutableAttributedString) #expect(!(fooBar is NSMutableAttributedString))
XCTAssertEqual(fooBar.string, "foobar") #expect(fooBar.string == "foobar")
XCTAssertEqual(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof") #expect(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
XCTAssertNil(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil)) #expect(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
} }
func testAdditionEqual() throws { @Test func addEqual() throws {
var fooBar = NSAttributedString(string: "foo", attributes: [.toolTip: "moof"]) var fooBar = NSAttributedString(string: "foo", attributes: [.toolTip: "moof"])
fooBar += NSAttributedString(string: "bar", attributes: [:]) fooBar += NSAttributedString(string: "bar", attributes: [:])
XCTAssertFalse(fooBar is NSMutableAttributedString) #expect(!(fooBar is NSMutableAttributedString))
XCTAssertEqual(fooBar.string, "foobar") #expect(fooBar.string == "foobar")
XCTAssertEqual(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof") #expect(fooBar.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
XCTAssertNil(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil)) #expect(fooBar.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
} }
func testTruncation() throws { @Test func truncate() throws {
let string1 = NSMutableAttributedString(string: "0123456") let string1 = NSMutableAttributedString(string: "0123456")
string1.truncateHead(until: 5, offset: 2) string1.truncateHead(until: 5, offset: 2)
XCTAssertEqual(string1.string, "…3456") #expect(string1.string == "…3456")
let string2 = NSMutableAttributedString(string: "0123456") let string2 = NSMutableAttributedString(string: "0123456")
string2.truncateHead(until: 2, offset: 3) string2.truncateHead(until: 2, offset: 3)
XCTAssertEqual(string2.string, "0123456") #expect(string2.string == "0123456")
let string3 = NSMutableAttributedString(string: "🐱🐶🐮") let string3 = NSMutableAttributedString(string: "🐱🐶🐮")
string3.truncateHead(until: 4, offset: 1) string3.truncateHead(until: 4, offset: 1)
XCTAssertEqual(string3.string, "…🐶🐮") #expect(string3.string == "…🐶🐮")
let string4 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄") let string4 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string4.truncateHead(until: 4, offset: 1) string4.truncateHead(until: 4, offset: 1)
XCTAssertEqual(string4.string, "🐈‍⬛🐕🐄") #expect(string4.string == "🐈‍⬛🐕🐄")
let string5 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄") let string5 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string5.truncateHead(until: 5, offset: 1) string5.truncateHead(until: 5, offset: 1)
XCTAssertEqual(string5.string, "🐈‍⬛🐕🐄") #expect(string5.string == "🐈‍⬛🐕🐄")
let string6 = NSMutableAttributedString(string: "🐈⬛ab") let string6 = NSMutableAttributedString(string: "🐈⬛ab")
string6.truncateHead(until: 5, offset: 1) string6.truncateHead(until: 5, offset: 1)
XCTAssertEqual(string6.string, "…ab") #expect(string6.string == "…ab")
let string7 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄") let string7 = NSMutableAttributedString(string: "🐈‍⬛🐕🐄")
string7.truncateHead(until: 6, offset: 1) string7.truncateHead(until: 6, offset: 1)
XCTAssertEqual(string7.string, "…🐕🐄") #expect(string7.string == "…🐕🐄")
} }
func testJoin() { @Test func join() {
let attrs: [NSAttributedString] = [ let attrs: [NSAttributedString] = [
NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"]), NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"]),
@ -95,26 +96,26 @@ final class NSAttributedStringTests: XCTestCase {
let space = NSAttributedString(string: " ", attributes: [.toolTip: "space"]) let space = NSAttributedString(string: " ", attributes: [.toolTip: "space"])
let joined = attrs.joined() let joined = attrs.joined()
XCTAssertFalse(joined is NSMutableAttributedString) #expect(!(joined is NSMutableAttributedString))
XCTAssertEqual(joined.string, "foobarbuz") #expect(joined.string == "foobarbuz")
XCTAssertEqual(joined.attribute(.toolTip, at: 1, effectiveRange: nil) as? String, "moof") #expect(joined.attribute(.toolTip, at: 1, effectiveRange: nil) as? String == "moof")
XCTAssertNil(joined.attribute(.toolTip, at: 3, effectiveRange: nil)) #expect(joined.attribute(.toolTip, at: 3, effectiveRange: nil) == nil)
let spaceJoined = attrs.joined(separator: space) let spaceJoined = attrs.joined(separator: space)
XCTAssertFalse(spaceJoined is NSMutableAttributedString) #expect(!(spaceJoined is NSMutableAttributedString))
XCTAssertEqual(spaceJoined.string, "foo bar buz") #expect(spaceJoined.string == "foo bar buz")
XCTAssertEqual(spaceJoined.attribute(.toolTip, at: 0, effectiveRange: nil) as? String, "moof") #expect(spaceJoined.attribute(.toolTip, at: 0, effectiveRange: nil) as? String == "moof")
XCTAssertEqual(spaceJoined.attribute(.toolTip, at: 3, effectiveRange: nil) as? String, "space") #expect(spaceJoined.attribute(.toolTip, at: 3, effectiveRange: nil) as? String == "space")
XCTAssertNil(spaceJoined.attribute(.toolTip, at: 4, effectiveRange: nil)) #expect(spaceJoined.attribute(.toolTip, at: 4, effectiveRange: nil) == nil)
let empty: [NSAttributedString] = [] let empty: [NSAttributedString] = []
let emptyJoined = empty.joined(separator: space) let emptyJoined = empty.joined(separator: space)
XCTAssertFalse(emptyJoined is NSMutableAttributedString) #expect(!(emptyJoined is NSMutableAttributedString))
XCTAssertEqual(emptyJoined.string, "") #expect(emptyJoined.string.isEmpty)
let single: [NSAttributedString] = [NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"])] let single: [NSAttributedString] = [NSMutableAttributedString(string: "foo", attributes: [.toolTip: "moof"])]
let singleJoined = single.joined(separator: space) let singleJoined = single.joined(separator: space)
XCTAssertFalse(singleJoined is NSMutableAttributedString) #expect(!(singleJoined is NSMutableAttributedString))
XCTAssertEqual(singleJoined.string, "foo") #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,107 +24,94 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit
import Testing
@testable import CotEditor @testable import CotEditor
final class NSLayoutManagerTests: XCTestCase { struct NSLayoutManagerTests {
func testTemporaryAttributeExistence() { @Test func checkTemporaryAttribute() {
let layoutManager = NSLayoutManager() let layoutManager = NSLayoutManager()
let textStorage = NSTextStorage(string: "cat dog cow") let textStorage = NSTextStorage(string: "cat dog cow")
textStorage.addLayoutManager(layoutManager) textStorage.addLayoutManager(layoutManager)
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor)) #expect(!layoutManager.hasTemporaryAttribute(.foregroundColor))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<0))) #expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<0)))
layoutManager.addTemporaryAttribute(.foregroundColor, value: NSColor.green, forCharacterRange: NSRange(4..<7)) layoutManager.addTemporaryAttribute(.foregroundColor, value: NSColor.green, forCharacterRange: NSRange(4..<7))
layoutManager.addTemporaryAttribute(.backgroundColor, value: NSColor.white, forCharacterRange: NSRange(6..<8)) layoutManager.addTemporaryAttribute(.backgroundColor, value: NSColor.white, forCharacterRange: NSRange(6..<8))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor)) #expect(layoutManager.hasTemporaryAttribute(.foregroundColor))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<4))) #expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(..<4)))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(3..<6))) #expect(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(3..<6)))
XCTAssertTrue(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(6..<8))) #expect(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(6..<8)))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<7))) #expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<7)))
XCTAssertFalse(layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<textStorage.length))) #expect(!layoutManager.hasTemporaryAttribute(.foregroundColor, in: NSRange(7..<textStorage.length)))
} }
func testLeftCharacterIndex() { @Test func characterIndex() {
let layoutManager = NSLayoutManager() let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer()) layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "dog\r\n 🐕") let textStorage = NSTextStorage(string: "dog\r\n 🐕")
textStorage.addLayoutManager(layoutManager) textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 0) // left characters
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 0) #expect(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 0)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 1) #expect(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 0)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 2) #expect(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 2) #expect(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 2)
#expect(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 2)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 3) #expect(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 5) #expect(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 5) #expect(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 6) #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() { @Test func bidiCharacterIndex() {
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() {
let layoutManager = NSLayoutManager() let layoutManager = NSLayoutManager()
layoutManager.addTextContainer(NSTextContainer()) layoutManager.addTextContainer(NSTextContainer())
let textStorage = NSTextStorage(string: "كلب\r\n 🐕") let textStorage = NSTextStorage(string: "كلب\r\n 🐕")
textStorage.addLayoutManager(layoutManager) textStorage.addLayoutManager(layoutManager)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight), 1) // left characters
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight), 2) #expect(layoutManager.leftCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight), 3) #expect(layoutManager.leftCharacterIndex(of: 1, baseWritingDirection: .leftToRight) == 2)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight), 0) #expect(layoutManager.leftCharacterIndex(of: 2, baseWritingDirection: .leftToRight) == 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight), 1) #expect(layoutManager.leftCharacterIndex(of: 3, baseWritingDirection: .leftToRight) == 0)
#expect(layoutManager.leftCharacterIndex(of: 4, baseWritingDirection: .leftToRight) == 1)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight), 3) #expect(layoutManager.leftCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 3)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight), 5) #expect(layoutManager.leftCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight), 5) #expect(layoutManager.leftCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 5)
XCTAssertEqual(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight), 6) #expect(layoutManager.leftCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 6)
}
// right characters
#expect(layoutManager.rightCharacterIndex(of: 0, baseWritingDirection: .leftToRight) == 5)
func testBidiRightCharacterIndex() { #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)
let layoutManager = NSLayoutManager() #expect(layoutManager.rightCharacterIndex(of: 5, baseWritingDirection: .leftToRight) == 6)
layoutManager.addTextContainer(NSTextContainer()) #expect(layoutManager.rightCharacterIndex(of: 6, baseWritingDirection: .leftToRight) == 8)
let textStorage = NSTextStorage(string: "كلب\r\n 🐕") #expect(layoutManager.rightCharacterIndex(of: 7, baseWritingDirection: .leftToRight) == 8)
textStorage.addLayoutManager(layoutManager) #expect(layoutManager.rightCharacterIndex(of: 8, baseWritingDirection: .leftToRight) == 8)
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)
} }
} }

View File

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

View File

@ -23,10 +23,11 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class OutlineTests: XCTestCase { struct OutlineTests {
private let items: [OutlineItem] = [ private let items: [OutlineItem] = [
OutlineItem(title: "dog", range: NSRange(location: 10, length: 5)), // 0 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)) #expect(self.items.item(at: 9) == nil)
XCTAssertEqual(self.items.item(at: 10), self.items[0]) #expect(self.items.item(at: 10) == self.items[0])
XCTAssertEqual(self.items.item(at: 18), self.items[0]) #expect(self.items.item(at: 18) == self.items[0])
XCTAssertEqual(self.items.item(at: 20), self.items[0]) #expect(self.items.item(at: 20) == self.items[0])
XCTAssertEqual(self.items.item(at: 40), self.items[3]) #expect(self.items.item(at: 40) == self.items[3])
XCTAssertEqual(self.items.item(at: 50), self.items[3]) #expect(self.items.item(at: 50) == self.items[3])
XCTAssertEqual(self.items.item(at: 59), self.items[3]) #expect(self.items.item(at: 59) == self.items[3])
XCTAssertEqual(self.items.item(at: 60), self.items[5]) #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))) #expect(self.items.previousItem(for: NSRange(10..<20)) == nil)
XCTAssertNil(self.items.previousItem(for: NSRange(19..<19))) #expect(self.items.previousItem(for: NSRange(19..<19)) == nil)
XCTAssertEqual(self.items.previousItem(for: NSRange(59..<70)), items[0]) #expect(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(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]) #expect(self.items.nextItem(for: NSRange(0..<0)) == items[0])
XCTAssertEqual(self.items.nextItem(for: NSRange(0..<10)), items[3]) #expect(self.items.nextItem(for: NSRange(0..<10)) == items[3])
XCTAssertEqual(self.items.nextItem(for: NSRange(40..<40)), items[5]) #expect(self.items.nextItem(for: NSRange(40..<40)) == items[5])
XCTAssertNil(self.items.nextItem(for: NSRange(60..<60))) #expect(self.items.nextItem(for: NSRange(60..<60)) == nil)
XCTAssertNil(self.items.nextItem(for: NSRange(40..<61))) #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) #expect(self.items.compactMap { $0.filter("", keyPath: \.title) }.count == 7)
XCTAssertEqual(self.items.compactMap { $0.filter("cat", keyPath: \.title) }.count, 0) #expect(self.items.compactMap { $0.filter("cat", keyPath: \.title) }.count == 0)
XCTAssertEqual(self.items.compactMap { $0.filter("dog", keyPath: \.title) }.count, 2) #expect(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("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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,64 +23,68 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @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. // -> Only the `]` at the first position will be evaluated as a character.
let character = RegularExpressionSyntaxType.character let character = RegularExpressionSyntaxType.character
XCTAssertEqual(character.ranges(in: "[abc]"), [NSRange(location: 1, length: 3)]) #expect(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)]) #expect(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)]) #expect(character.ranges(in: "[a\\]]") == [NSRange(location: 2, length: 2), NSRange(location: 1, length: 3)])
XCTAssertEqual(character.ranges(in: "[]]"), [NSRange(location: 1, length: 1)]) #expect(character.ranges(in: "[]]") == [NSRange(location: 1, length: 1)])
XCTAssertEqual(character.ranges(in: "[a]]"), [NSRange(location: 1, length: 1)]) #expect(character.ranges(in: "[a]]") == [NSRange(location: 1, length: 1)])
XCTAssertEqual(character.ranges(in: "[]a]"), [NSRange(location: 1, length: 2)]) #expect(character.ranges(in: "[]a]") == [NSRange(location: 1, length: 2)])
XCTAssertEqual(character.ranges(in: "[a]b]"), [NSRange(location: 1, length: 1)]) #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)]) NSRange(location: 5, length: 1)])
XCTAssertEqual(character.ranges(in: "[^a]"), [NSRange(location: 2, length: 1)]) #expect(character.ranges(in: "[^a]") == [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^^]"), [NSRange(location: 2, length: 1)]) #expect(character.ranges(in: "[^^]") == [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]]"), [NSRange(location: 2, length: 1)]) #expect(character.ranges(in: "[^]]") == [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]]]"), [NSRange(location: 2, length: 1)]) #expect(character.ranges(in: "[^]]]") == [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^a]]"), [NSRange(location: 2, length: 1)]) #expect(character.ranges(in: "[^a]]") == [NSRange(location: 2, length: 1)])
XCTAssertEqual(character.ranges(in: "[^]a]"), [NSRange(location: 2, length: 2)]) #expect(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]b]") == [NSRange(location: 2, length: 1)])
// just containing ranges for `\[` // 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 let symbol = RegularExpressionSyntaxType.symbol
XCTAssertEqual(symbol.ranges(in: "[abc]"), [NSRange(location: 0, length: 5)]) #expect(symbol.ranges(in: "[abc]") == [NSRange(location: 0, length: 5)])
XCTAssertEqual(symbol.ranges(in: "\\[a[a]"), [NSRange(location: 3, length: 3)]) #expect(symbol.ranges(in: "\\[a[a]") == [NSRange(location: 3, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[a\\]]"), [NSRange(location: 0, length: 5)]) #expect(symbol.ranges(in: "[a\\]]") == [NSRange(location: 0, length: 5)])
XCTAssertEqual(symbol.ranges(in: "[]]"), [NSRange(location: 0, length: 3)]) #expect(symbol.ranges(in: "[]]") == [NSRange(location: 0, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[a]]"), [NSRange(location: 0, length: 3)]) #expect(symbol.ranges(in: "[a]]") == [NSRange(location: 0, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[]a]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[]a]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[a]b]"), [NSRange(location: 0, length: 3)]) #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)]) NSRange(location: 4, length: 3)])
XCTAssertEqual(symbol.ranges(in: "[^a]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[^a]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^^]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[^^]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[^]]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]]]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[^]]]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^a]]"), [NSRange(location: 0, length: 4)]) #expect(symbol.ranges(in: "[^a]]") == [NSRange(location: 0, length: 4)])
XCTAssertEqual(symbol.ranges(in: "[^]a]"), [NSRange(location: 0, length: 5)]) #expect(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]b]") == [NSRange(location: 0, length: 4)])
// just containing ranges for `(?<=`, `(` and `)` // 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: 0, length: 1),
NSRange(location: 6, length: 1)]) NSRange(location: 6, length: 1)])
} }
} }

View File

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

View File

@ -9,7 +9,7 @@
// //
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// //
// © 2016-2023 1024jp // © 2016-2024 1024jp
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,94 +24,95 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit
import Testing
@testable import CotEditor @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])) Shortcut("a", modifiers: [.control, .shift]))
XCTAssertEqual(Shortcut(keySpecChars: "^A"), #expect(Shortcut(keySpecChars: "^A") ==
Shortcut(keySpecChars: "^$a")) Shortcut(keySpecChars: "^$a"))
} }
func testKeySpecCharsCreation() { @Test func createKeySpecChars() {
XCTAssertNil(Shortcut("", modifiers: [])) #expect(Shortcut("", modifiers: []) == nil)
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars, "^$a") #expect(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars == "^$a")
XCTAssertEqual(Shortcut("b", modifiers: [.command, .option])?.keySpecChars, "~@b") #expect(Shortcut("b", modifiers: [.command, .option])?.keySpecChars == "~@b")
XCTAssertEqual(Shortcut("A", modifiers: [.control])?.keySpecChars, "^$a") // uppercase for Shift key #expect(Shortcut("A", modifiers: [.control])?.keySpecChars == "^$a") // uppercase for Shift key
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars, "^$a") #expect(Shortcut("a", modifiers: [.control, .shift])?.keySpecChars == "^$a")
XCTAssertEqual(Shortcut("a", modifiers: [])?.keySpecChars, "a") #expect(Shortcut("a", modifiers: [])?.keySpecChars == "a")
XCTAssertEqual(Shortcut("a", modifiers: [])?.isValid, false) #expect(Shortcut("a", modifiers: [])?.isValid == false)
XCTAssertNil(Shortcut("", modifiers: [.control, .shift])) #expect(Shortcut("", modifiers: [.control, .shift]) == nil)
XCTAssertEqual(Shortcut("a", modifiers: [.control, .shift])?.isValid, true) #expect(Shortcut("a", modifiers: [.control, .shift])?.isValid == true)
XCTAssertEqual(Shortcut("ab", modifiers: [.control, .shift])?.isValid, false) #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") #expect(shortcut.keyEquivalent == "a")
XCTAssertEqual(shortcut.modifiers, [.control, .shift]) #expect(shortcut.modifiers == [.control, .shift])
XCTAssert(shortcut.isValid) #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) #expect(!shortcut.isValid)
XCTAssertEqual(shortcut.keyEquivalent, "a") #expect(shortcut.keyEquivalent == "a")
XCTAssertEqual(shortcut.modifiers, [.function]) #expect(shortcut.modifiers == [.function])
XCTAssert(shortcut.symbol == "fnA" || shortcut.symbol == "🌐A") #expect(shortcut.symbol == "fnA" || shortcut.symbol == "🌐A")
XCTAssertEqual(shortcut.keySpecChars, "a", "The fn key should be ignored.") #expect(shortcut.keySpecChars == "a", "The fn key should be ignored.")
let symbolName = try XCTUnwrap(shortcut.modifierSymbolNames.first) let symbolName = try #require(shortcut.modifierSymbolNames.first)
XCTAssertNotNil(NSImage(systemSymbolName: symbolName, accessibilityDescription: nil)) #expect(NSImage(systemSymbolName: symbolName, accessibilityDescription: nil) != nil)
} }
func testMenuItemShortcut() { @Test func menuItemShortcut() {
let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "C") let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "C")
menuItem.keyEquivalentModifierMask = [.command] menuItem.keyEquivalentModifierMask = [.command]
let shortcut = Shortcut(menuItem.keyEquivalent, modifiers: menuItem.keyEquivalentModifierMask) let shortcut = Shortcut(menuItem.keyEquivalent, modifiers: menuItem.keyEquivalentModifierMask)
XCTAssertEqual(shortcut?.symbol, "C") #expect(shortcut?.symbol == "C")
XCTAssertEqual(shortcut, menuItem.shortcut) #expect(shortcut == menuItem.shortcut)
} }
func testShortcutSymbols() throws { @Test func shortcutSymbols() throws {
// test modifier symbols // test modifier symbols
XCTAssertNil(Shortcut(keySpecChars: "")) #expect(Shortcut(keySpecChars: "") == nil)
XCTAssertEqual(Shortcut(keySpecChars: "^$a")?.symbol, "^A") #expect(Shortcut(keySpecChars: "^$a")?.symbol == "^A")
XCTAssertEqual(Shortcut(keySpecChars: "~@b")?.symbol, "B") #expect(Shortcut(keySpecChars: "~@b")?.symbol == "B")
// test unprintable keys // test unprintable keys
let f10 = try XCTUnwrap(String(NSEvent.SpecialKey.f10.unicodeScalar)) let f10 = try #require(String(NSEvent.SpecialKey.f10.unicodeScalar))
XCTAssertEqual(Shortcut(keySpecChars: "@" + f10)?.symbol, "F10") #expect(Shortcut(keySpecChars: "@" + f10)?.symbol == "F10")
let delete = try XCTUnwrap(UnicodeScalar(NSDeleteCharacter).flatMap(String.init)) let delete = try #require(UnicodeScalar(NSDeleteCharacter).flatMap(String.init))
XCTAssertEqual(Shortcut(keySpecChars: "@" + delete)?.symbol, "⌘ ⌫") #expect(Shortcut(keySpecChars: "@" + delete)?.symbol == "⌘ ⌫")
// test creation // test creation
let deleteForward = try XCTUnwrap(String(NSEvent.SpecialKey.deleteForward.unicodeScalar)) let deleteForward = try #require(String(NSEvent.SpecialKey.deleteForward.unicodeScalar))
XCTAssertNil(Shortcut(symbolRepresentation: "")) #expect(Shortcut(symbolRepresentation: "") == nil)
XCTAssertEqual(Shortcut(symbolRepresentation: "^A")?.keySpecChars, "^$a") #expect(Shortcut(symbolRepresentation: "^A")?.keySpecChars == "^$a")
XCTAssertEqual(Shortcut(symbolRepresentation: "B")?.keySpecChars, "~@b") #expect(Shortcut(symbolRepresentation: "B")?.keySpecChars == "~@b")
XCTAssertEqual(Shortcut(symbolRepresentation: "F10")?.keySpecChars, "@" + f10) #expect(Shortcut(symbolRepresentation: "F10")?.keySpecChars == "@" + f10)
XCTAssertEqual(Shortcut(symbolRepresentation: "⌘ ⌦")?.keySpecChars, "@" + deleteForward) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,22 +23,23 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class SnippetTests: XCTestCase { struct SnippetTests {
func testSimpleSnippet() { @Test func simpleSnippet() {
let snippet = Snippet(name: "", format: "<h1><<<SELECTION>>><<<CURSOR>>></h1>") let snippet = Snippet(name: "", format: "<h1><<<SELECTION>>><<<CURSOR>>></h1>")
let (string, selections) = snippet.insertion(selectedString: "abc") let (string, selections) = snippet.insertion(selectedString: "abc")
XCTAssertEqual(string, "<h1>abc</h1>") #expect(string == "<h1>abc</h1>")
XCTAssertEqual(selections, [NSRange(location: 7, length: 0)]) #expect(selections == [NSRange(location: 7, length: 0)])
} }
func testMultipleLines() { @Test func multipleLines() {
let format = """ let format = """
<ul> <ul>
@ -55,9 +56,9 @@ final class SnippetTests: XCTestCase {
<li></li> <li></li>
</ul> </ul>
""" """
XCTAssertEqual(string, expectedString) #expect(string == expectedString)
XCTAssertEqual(selections, [NSRange(location: 13, length: 0), #expect(selections == [NSRange(location: 13, length: 0),
NSRange(location: 27, length: 0)]) NSRange(location: 27, length: 0)])
let (indentedString, indentedSelections) = snippet.insertion(selectedString: "", indent: " ") let (indentedString, indentedSelections) = snippet.insertion(selectedString: "", indent: " ")
@ -67,13 +68,13 @@ final class SnippetTests: XCTestCase {
<li></li> <li></li>
</ul> </ul>
""" """
XCTAssertEqual(indentedString, expectedIndentString) #expect(indentedString == expectedIndentString)
XCTAssertEqual(indentedSelections, [NSRange(location: 17, length: 0), #expect(indentedSelections == [NSRange(location: 17, length: 0),
NSRange(location: 35, length: 0)]) NSRange(location: 35, length: 0)])
} }
func testMultipleInsertions() { @Test func multipleInsertions() {
let string = """ let string = """
aaa aaa
@ -95,7 +96,7 @@ final class SnippetTests: XCTestCase {
let expectedSelections = [NSRange(location: 11, length: 0), let expectedSelections = [NSRange(location: 11, length: 0),
NSRange(location: 21, length: 0), NSRange(location: 21, length: 0),
NSRange(location: 33, length: 0)] NSRange(location: 33, length: 0)]
XCTAssertEqual(strings, expectedStrings) #expect(strings == expectedStrings)
XCTAssertEqual(selections, expectedSelections) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,21 +24,21 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Testing
@testable import CotEditor @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 names = ["foo", "foo 3", "foo copy 3", "foo 4", "foo 7"]
let copy = "copy" let copy = "copy"
XCTAssertEqual(names.createAvailableName(for: "foo"), "foo 2") #expect(names.createAvailableName(for: "foo") == "foo 2")
XCTAssertEqual(names.createAvailableName(for: "foo 3"), "foo 5") #expect(names.createAvailableName(for: "foo 3") == "foo 5")
XCTAssertEqual(names.createAvailableName(for: "foo", suffix: copy), "foo copy") #expect(names.createAvailableName(for: "foo", suffix: copy) == "foo copy")
XCTAssertEqual(names.createAvailableName(for: "foo 3", suffix: copy), "foo 3 copy") #expect(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 copy 3", suffix: copy) == "foo copy 4")
} }
} }

View File

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

View File

@ -24,28 +24,27 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class StringExtensionsTests: XCTestCase { struct StringExtensionsTests {
/// Tests if the U+FEFF omitting bug on Swift 5 still exists. /// Tests if the U+FEFF omitting bug on Swift 5 still exists.
/// @Test(.bug("https://bugs.swift.org/browse/SR-10896")) func feff() {
/// - Bug: <https://bugs.swift.org/browse/SR-10896>
func testFEFF() {
let bom = "\u{feff}" let bom = "\u{feff}"
// -> Some of these test cases must fail if the bug fixed. // -> Some of these test cases must fail if the bug fixed.
XCTAssertEqual(bom.count, 1) #expect(bom.count == 1)
XCTAssertEqual(("\(bom)abc").count, 4) #expect(("\(bom)abc").count == 4)
XCTAssertEqual(NSString(string: bom).length, 0) // correct: 1 #expect(NSString(string: bom).length == 0) // correct: 1
XCTAssertEqual(NSString(string: "\(bom)\(bom)").length, 1) // correct: 2 #expect(NSString(string: "\(bom)\(bom)").length == 1) // correct: 2
XCTAssertEqual(NSString(string: "\(bom)abc").length, 3) // correct: 4 #expect(NSString(string: "\(bom)abc").length == 3) // correct: 4
XCTAssertEqual(NSString(string: "a\(bom)bc").length, 4) #expect(NSString(string: "a\(bom)bc").length == 4)
let string = "\(bom)abc" 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. // Implicit NSString cast is fixed.
// -> However, still crashes when `string.immutable.enumerateSubstrings(in:)` // -> 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" let string = "a\\a\\\\aa"
XCTAssertFalse(string.isCharacterEscaped(at: 0)) #expect(!string.isCharacterEscaped(at: 0))
XCTAssertTrue(string.isCharacterEscaped(at: 2)) #expect(string.isCharacterEscaped(at: 2))
XCTAssertFalse(string.isCharacterEscaped(at: 5)) #expect(!string.isCharacterEscaped(at: 5))
} }
func testUnescaping() { @Test func unescape() {
XCTAssertEqual(#"\\"#.unescaped, "\\") #expect(#"\\"#.unescaped == "\\")
XCTAssertEqual(#"\'"#.unescaped, "\'") #expect(#"\'"#.unescaped == "\'")
XCTAssertEqual(#"\""#.unescaped, "\"") #expect(#"\""#.unescaped == "\"")
XCTAssertEqual(#"a\n "#.unescaped, "a\n ") #expect(#"a\n "#.unescaped == "a\n ")
XCTAssertEqual(#"a\\n "#.unescaped, "a\\n ") #expect(#"a\\n "#.unescaped == "a\\n ")
XCTAssertEqual(#"a\\\n "#.unescaped, "a\\\n ") #expect(#"a\\\n "#.unescaped == "a\\\n ")
XCTAssertEqual(#"a\\\\n"#.unescaped, "a\\\\n") #expect(#"a\\\\n"#.unescaped == "a\\\\n")
XCTAssertEqual(#"\\\\\t"#.unescaped, "\\\\\t") #expect(#"\\\\\t"#.unescaped == "\\\\\t")
XCTAssertEqual(#"\\foo\\\\\0bar\\"#.unescaped, "\\foo\\\\\u{0}bar\\") #expect(#"\\foo\\\\\0bar\\"#.unescaped == "\\foo\\\\\u{0}bar\\")
XCTAssertEqual(#"\\\\\\\\foo"#.unescaped, "\\\\\\\\foo") #expect(#"\\\\\\\\foo"#.unescaped == "\\\\\\\\foo")
XCTAssertEqual(#"foo: \r\n1"#.unescaped, "foo: \r\n1") #expect(#"foo: \r\n1"#.unescaped == "foo: \r\n1")
} }
func testComposedCharactersCount() { @Test func countComposedCharacters() {
// make sure that `String.count` counts characters as I want // make sure that `String.count` counts characters as I want
XCTAssertEqual("foo".count, 3) #expect("foo".count == 3)
XCTAssertEqual("\r\n".count, 1) #expect("\r\n".count == 1)
XCTAssertEqual("😀🇯🇵a".count, 3) #expect("😀🇯🇵a".count == 3)
XCTAssertEqual("😀🏻".count, 1) #expect("😀🏻".count == 1)
XCTAssertEqual("👍🏻".count, 1) #expect("👍🏻".count == 1)
// single regional indicator // single regional indicator
XCTAssertEqual("🇦 ".count, 2) #expect("🇦 ".count == 2)
} }
func testWordsCount() { @Test func countWords() {
XCTAssertEqual("Clarus says moof!".numberOfWords, 3) #expect("Clarus says moof!".numberOfWords == 3)
XCTAssertEqual("plain-text".numberOfWords, 2) #expect("plain-text".numberOfWords == 2)
XCTAssertEqual("!".numberOfWords, 0) #expect("!".numberOfWords == 0)
XCTAssertEqual("".numberOfWords, 0) #expect("".numberOfWords == 0)
} }
func testLinesCount() { @Test func countLines() {
XCTAssertEqual("".numberOfLines, 0) #expect("".numberOfLines == 0)
XCTAssertEqual("a".numberOfLines, 1) #expect("a".numberOfLines == 1)
XCTAssertEqual("\n".numberOfLines, 1) #expect("\n".numberOfLines == 1)
XCTAssertEqual("\n\n".numberOfLines, 2) #expect("\n\n".numberOfLines == 2)
XCTAssertEqual("\u{feff}".numberOfLines, 1) #expect("\u{feff}".numberOfLines == 1)
XCTAssertEqual("ab\r\ncd".numberOfLines, 2) #expect("ab\r\ncd".numberOfLines == 2)
let testString = "a\nb c\n\n" let testString = "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines, 3) #expect(testString.numberOfLines == 3)
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0)), 0) // "" #expect(testString.numberOfLines(in: NSRange(0..<0)) == 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1)), 1) // "a" #expect(testString.numberOfLines(in: NSRange(0..<1)) == 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2)), 1) // "a\n" #expect(testString.numberOfLines(in: NSRange(0..<2)) == 1) // "a\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<6)), 2) // "a\nb c\n" #expect(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(in: NSRange(0..<7)) == 3) // "a\nb c\n\n"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true), 0) // "" #expect(testString.numberOfLines(in: NSRange(0..<0), includesLastBreak: true) == 0) // ""
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true), 1) // "a" #expect(testString.numberOfLines(in: NSRange(0..<1), includesLastBreak: true) == 1) // "a"
XCTAssertEqual(testString.numberOfLines(in: NSRange(0..<2), includesLastBreak: true), 2) // "a\n" #expect(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" #expect(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..<7), includesLastBreak: true) == 4) // "a\nb c\n\n"
XCTAssertEqual(testString.lineNumber(at: 0), 1) #expect(testString.lineNumber(at: 0) == 1)
XCTAssertEqual(testString.lineNumber(at: 1), 1) #expect(testString.lineNumber(at: 1) == 1)
XCTAssertEqual(testString.lineNumber(at: 2), 2) #expect(testString.lineNumber(at: 2) == 2)
XCTAssertEqual(testString.lineNumber(at: 5), 2) #expect(testString.lineNumber(at: 5) == 2)
XCTAssertEqual(testString.lineNumber(at: 6), 3) #expect(testString.lineNumber(at: 6) == 3)
XCTAssertEqual(testString.lineNumber(at: 7), 4) #expect(testString.lineNumber(at: 7) == 4)
let nsString = testString as NSString let nsString = testString as NSString
XCTAssertEqual(nsString.lineNumber(at: 0), testString.lineNumber(at: 0)) #expect(nsString.lineNumber(at: 0) == testString.lineNumber(at: 0))
XCTAssertEqual(nsString.lineNumber(at: 1), testString.lineNumber(at: 1)) #expect(nsString.lineNumber(at: 1) == testString.lineNumber(at: 1))
XCTAssertEqual(nsString.lineNumber(at: 2), testString.lineNumber(at: 2)) #expect(nsString.lineNumber(at: 2) == testString.lineNumber(at: 2))
XCTAssertEqual(nsString.lineNumber(at: 5), testString.lineNumber(at: 5)) #expect(nsString.lineNumber(at: 5) == testString.lineNumber(at: 5))
XCTAssertEqual(nsString.lineNumber(at: 6), testString.lineNumber(at: 6)) #expect(nsString.lineNumber(at: 6) == testString.lineNumber(at: 6))
XCTAssertEqual(nsString.lineNumber(at: 7), testString.lineNumber(at: 7)) #expect(nsString.lineNumber(at: 7) == testString.lineNumber(at: 7))
XCTAssertEqual("\u{FEFF}".numberOfLines(in: NSRange(0..<1)), 1) // "\u{FEFF}" #expect("\u{FEFF}".numberOfLines(in: NSRange(0..<1)) == 1) // "\u{FEFF}"
XCTAssertEqual("\u{FEFF}\nb".numberOfLines(in: NSRange(0..<3)), 2) // "\u{FEFF}\nb" #expect("\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" #expect("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("a\u{FEFF}\u{FEFF}\nb".numberOfLines(in: NSRange(1..<5)) == 2) // "\u{FEFF}\nb"
XCTAssertEqual("a\u{FEFF}\nb".numberOfLines, 2) #expect("a\u{FEFF}\nb".numberOfLines == 2)
XCTAssertEqual("\u{FEFF}\nb".numberOfLines, 2) #expect("\u{FEFF}\nb".numberOfLines == 2)
XCTAssertEqual("\u{FEFF}0000000000000000".numberOfLines, 1) #expect("\u{FEFF}0000000000000000".numberOfLines == 1)
let bomString = "\u{FEFF}\nb" let bomString = "\u{FEFF}\nb"
let range = bomString.startIndex..<bomString.index(bomString.startIndex, offsetBy: 2) 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🐱 " let string = "aaa \r\n🐱 "
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)), 3) #expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 3)) == 3)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)), 4) #expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 4)) == 4)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)), 0) #expect(string.columnNumber(at: string.index(string.startIndex, offsetBy: 5)) == 0)
XCTAssertEqual(string.columnNumber(at: string.index(string.startIndex, offsetBy: 6)), 1) #expect(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: 7)) == 2)
} }
func testCharacterCountOptions() { @Test func countCharactersWithOptions() {
var options = CharacterCountOptions() var options = CharacterCountOptions()
let string = "aaa \t 🐱\n\r\n c" 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 options.ignoresNewlines = true
XCTAssertEqual(string.count(options: options), 9) #expect(string.count(options: options) == 9)
options.ignoresWhitespaces = true options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 5) #expect(string.count(options: options) == 5)
options.ignoresNewlines = false options.ignoresNewlines = false
options.ignoresWhitespaces = true options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 7) #expect(string.count(options: options) == 7)
// test .treatsConsecutiveWhitespaceAsSingle // test .treatsConsecutiveWhitespaceAsSingle
options = .init() options = .init()
options.treatsConsecutiveWhitespaceAsSingle = true options.treatsConsecutiveWhitespaceAsSingle = true
XCTAssertEqual(string.count(options: options), 7) #expect(string.count(options: options) == 7)
options.ignoresNewlines = true options.ignoresNewlines = true
XCTAssertEqual(string.count(options: options), 7) #expect(string.count(options: options) == 7)
options.treatsConsecutiveWhitespaceAsSingle = false options.treatsConsecutiveWhitespaceAsSingle = false
options.ignoresNewlines = true options.ignoresNewlines = true
options.ignoresWhitespaces = true options.ignoresWhitespaces = true
XCTAssertEqual(string.count(options: options), 5) #expect(string.count(options: options) == 5)
// test other units // test other units
options = .init() options = .init()
options.unit = .unicodeScalar options.unit = .unicodeScalar
XCTAssertEqual(string.count(options: options), 12) #expect(string.count(options: options) == 12)
options.unit = .utf16 options.unit = .utf16
XCTAssertEqual(string.count(options: options), 13) #expect(string.count(options: options) == 13)
// test normalization // test normalization
let aUmlaut = "" let aUmlaut = ""
options = .init() options = .init()
options.unit = .unicodeScalar options.unit = .unicodeScalar
XCTAssertEqual(aUmlaut.count(options: options), 2) #expect(aUmlaut.count(options: options) == 2)
options.normalizationForm = .nfc options.normalizationForm = .nfc
XCTAssertEqual(aUmlaut.count(options: options), 1) #expect(aUmlaut.count(options: options) == 1)
} }
func testByteCountOptions() { @Test func countBytes() {
let string = "abc犬牛" let string = "abc犬牛"
var options = CharacterCountOptions(unit: .byte) var options = CharacterCountOptions(unit: .byte)
options.encoding = .utf8 options.encoding = .utf8
XCTAssertEqual(string.count(options: options), 9) #expect(string.count(options: options) == 9)
options.encoding = .shiftJIS options.encoding = .shiftJIS
XCTAssertEqual(string.count(options: options), 7) #expect(string.count(options: options) == 7)
options.encoding = .ascii options.encoding = .ascii
XCTAssertNil(string.count(options: options)) #expect(string.count(options: options) == nil)
options.encoding = .nonLossyASCII 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") #expect("AbcDefg Hij".snakecased == "abc_defg hij")
XCTAssertEqual("abcDefg Hij".snakecased, "abc_defg hij") #expect("abcDefg Hij".snakecased == "abc_defg hij")
XCTAssertEqual("_abcDefg Hij".snakecased, "_abc_defg hij") #expect("_abcDefg Hij".snakecased == "_abc_defg hij")
XCTAssertEqual("AA\u{0308}".snakecased, "a_a\u{0308}") #expect("AA\u{0308}".snakecased == "a_a\u{0308}")
XCTAssertEqual("abÄb".snakecased, "ab_äb") #expect("abÄb".snakecased == "ab_äb")
XCTAssertEqual("abc_defg Hij".camelcased, "abcDefg hij") #expect("abc_defg Hij".camelcased == "abcDefg hij")
XCTAssertEqual("AbcDefg Hij".camelcased, "abcDefg hij") #expect("AbcDefg Hij".camelcased == "abcDefg hij")
XCTAssertEqual("_abcDefg Hij".camelcased, "_abcDefg hij") #expect("_abcDefg Hij".camelcased == "_abcDefg hij")
XCTAssertEqual("a_a\u{0308}".camelcased, "aA\u{0308}") #expect("a_a\u{0308}".camelcased == "aA\u{0308}")
XCTAssertEqual("abc_defg Hij".pascalcased, "AbcDefg Hij") #expect("abc_defg Hij".pascalcased == "AbcDefg Hij")
XCTAssertEqual("abcDefg Hij".pascalcased, "AbcDefg Hij") #expect("abcDefg Hij".pascalcased == "AbcDefg Hij")
XCTAssertEqual("_abcDefg Hij".pascalcased, "_abcDefg Hij") #expect("_abcDefg Hij".pascalcased == "_abcDefg Hij")
XCTAssertEqual("a_a\u{0308}".pascalcased, "AA\u{0308}") #expect("a_a\u{0308}".pascalcased == "AA\u{0308}")
} }
func testJapaneseTransform() { @Test func japaneseTransform() {
let testString = "犬 イヌ いぬ Dog 123 " let testString = "犬 イヌ いぬ Dog 123 "
XCTAssertEqual(testString.fullwidthRoman(reverse: false), "犬 イヌ いぬ ") #expect(testString.fullwidthRoman(reverse: false) == "犬 イヌ いぬ ")
XCTAssertEqual(testString.fullwidthRoman(reverse: true), "犬 イヌ いぬ Inu Dog 123 123") #expect(testString.fullwidthRoman(reverse: true) == "犬 イヌ いぬ Inu Dog 123 123")
} }
func testBeforeIndex() { @Test func beforeIndex() {
XCTAssertEqual(("00" as NSString).index(before: 0), 0) #expect(("00" as NSString).index(before: 0) == 0)
XCTAssertEqual(("00" as NSString).index(before: 1), 0) #expect(("00" as NSString).index(before: 1) == 0)
XCTAssertEqual(("00" as NSString).index(before: 2), 1) #expect(("00" as NSString).index(before: 2) == 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 1), 0) #expect(("0🇦🇦00" as NSString).index(before: 1) == 0)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 2), 1) #expect(("0🇦🇦00" as NSString).index(before: 2) == 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 5), 1) #expect(("0🇦🇦00" as NSString).index(before: 5) == 1)
XCTAssertEqual(("0🇦🇦00" as NSString).index(before: 6), 5) #expect(("0🇦🇦00" as NSString).index(before: 6) == 5)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 3), 1) #expect(("0\r\n0" as NSString).index(before: 3) == 1)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 2), 1) #expect(("0\r\n0" as NSString).index(before: 2) == 1)
XCTAssertEqual(("0\r\n0" as NSString).index(before: 1), 0) #expect(("0\r\n0" as NSString).index(before: 1) == 0)
XCTAssertEqual(("0\n" as NSString).index(before: 1), 0) #expect(("0\n" as NSString).index(before: 1) == 0)
XCTAssertEqual(("0\n" as NSString).index(before: 2), 1) #expect(("0\n" as NSString).index(before: 2) == 1)
} }
func testAfterIndex() { @Test func afterIndex() {
XCTAssertEqual(("00" as NSString).index(after: 0), 1) #expect(("00" as NSString).index(after: 0) == 1)
XCTAssertEqual(("00" as NSString).index(after: 1), 2) #expect(("00" as NSString).index(after: 1) == 2)
XCTAssertEqual(("00" as NSString).index(after: 2), 2) #expect(("00" as NSString).index(after: 2) == 2)
XCTAssertEqual(("0🇦🇦0" as NSString).index(after: 0), 1) #expect(("0🇦🇦0" as NSString).index(after: 0) == 1)
XCTAssertEqual(("0🇦🇦0" as NSString).index(after: 1), 5) #expect(("0🇦🇦0" as NSString).index(after: 1) == 5)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 1), 3) #expect(("0\r\n0" as NSString).index(after: 1) == 3)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 2), 3) #expect(("0\r\n0" as NSString).index(after: 2) == 3)
XCTAssertEqual(("0\r\n0" as NSString).index(after: 3), 4) #expect(("0\r\n0" as NSString).index(after: 3) == 4)
XCTAssertEqual(("0\r" as NSString).index(after: 1), 2) #expect(("0\r" as NSString).index(after: 1) == 2)
XCTAssertEqual(("0\r" as NSString).index(after: 2), 2) #expect(("0\r" as NSString).index(after: 2) == 2)
// composed character does not care CRLF // 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" 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)) 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)) 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)) string.startIndex..<string.index(string.startIndex, offsetBy: 3))
XCTAssertEqual((string as NSString).lineContentsRange(for: NSRange(..<1)), NSRange(..<3)) #expect((string as NSString).lineContentsRange(for: NSRange(..<1)) == NSRange(..<3))
XCTAssertEqual((string as NSString).lineContentsRange(at: 5), NSRange(5..<8)) #expect((string as NSString).lineContentsRange(at: 5) == NSRange(5..<8))
let emptyString = "" let emptyString = ""
let emptyRange = emptyString.startIndex..<emptyString.endIndex 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)]) #expect("foo\nbar".lineContentsRanges(for: NSRange(1..<1)) == [NSRange(1..<1)])
XCTAssertEqual("foo\nbar".lineContentsRanges(), [NSRange(0..<3), NSRange(4..<7)]) #expect("foo\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
XCTAssertEqual("foo\nbar\n".lineContentsRanges(), [NSRange(0..<3), NSRange(4..<7)]) #expect("foo\nbar\n".lineContentsRanges() == [NSRange(0..<3), NSRange(4..<7)])
XCTAssertEqual("foo\r\nbar".lineContentsRanges(), [NSRange(0..<3), NSRange(5..<8)]) #expect("foo\r\nbar".lineContentsRanges() == [NSRange(0..<3), NSRange(5..<8)])
XCTAssertEqual("foo\r\r\rbar".lineContentsRanges().count, 4) #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 set = CharacterSet(charactersIn: "._")
let string = "abc.d🐕f_ghij" as NSString let string = "abc.d🐕f_ghij" as NSString
XCTAssertEqual(string.substring(with: string.rangeOfCharacter(until: set, at: 0)), "abc") #expect(string.substring(with: string.rangeOfCharacter(until: set, at: 0)) == "abc")
XCTAssertEqual(string.substring(with: string.rangeOfCharacter(until: set, at: 4)), "d🐕f") #expect(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: string.length - 1)) == "ghij")
} }
func testComposedCharacterSequence() { @Test func composedCharacterSequence() {
let blackDog = "🐕‍⬛" // 4 let blackDog = "🐕‍⬛" // 4
XCTAssertEqual(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1), 0) #expect(blackDog.lowerBoundOfComposedCharacterSequence(2, offsetBy: 1) == 0)
let abcDog = "🐕⬛abc" // 4 1 1 1 let abcDog = "🐕⬛abc" // 4 1 1 1
XCTAssertEqual(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1), "🐕⬛a".utf16.count) #expect(abcDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕⬛a".utf16.count)
XCTAssertEqual(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1), "🐕‍⬛".utf16.count) #expect(abcDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == "🐕‍⬛".utf16.count)
let dogDog = "🐕‍⬛🐕" // 4 2 let dogDog = "🐕‍⬛🐕" // 4 2
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1), 0) #expect(dogDog.lowerBoundOfComposedCharacterSequence(5, offsetBy: 1) == 0)
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1), "🐕‍⬛".utf16.count) #expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 1) == "🐕‍⬛".utf16.count)
XCTAssertEqual(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0), "🐕‍⬛🐕".utf16.count) #expect(dogDog.lowerBoundOfComposedCharacterSequence(6, offsetBy: 0) == "🐕‍⬛🐕".utf16.count)
let string = "🐕🏴‍☠️🇯🇵🧑‍💻" // 2 5 4 5 let string = "🐕🏴‍☠️🇯🇵🧑‍💻" // 2 5 4 5
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3), 0) #expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 3) == 0)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2), 0) #expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 2) == 0)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1), "🐕".utf16.count) #expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 1) == "🐕".utf16.count)
XCTAssertEqual(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0), "🐕🏴‍☠️".utf16.count) #expect(string.lowerBoundOfComposedCharacterSequence(9, offsetBy: 0) == "🐕🏴‍☠️".utf16.count)
let abc = "abc" let abc = "abc"
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2), 0) #expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 2) == 0)
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1), 0) #expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 1) == 0)
XCTAssertEqual(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0), 1) #expect(abc.lowerBoundOfComposedCharacterSequence(1, offsetBy: 0) == 1)
} }
func testUnicodeNormalization() { @Test func normalizeUnicode() {
XCTAssertEqual("\t 神 ㍑ C".precomposedStringWithCompatibilityMappingWithCasefold, "é \t 神 リットル abc") #expect("\t 神 ㍑ C".precomposedStringWithCompatibilityMappingWithCasefold == "é \t 神 リットル abc")
XCTAssertEqual("\u{1f71} \u{03b1}\u{0301}".precomposedStringWithHFSPlusMapping, "\u{1f71} \u{03ac}") #expect("\u{1f71} \u{03b1}\u{0301}".precomposedStringWithHFSPlusMapping == "\u{1f71} \u{03ac}")
XCTAssertEqual("\u{1f71}".precomposedStringWithHFSPlusMapping, "\u{1f71}") // test single char #expect("\u{1f71}".precomposedStringWithHFSPlusMapping == "\u{1f71}") // test single char
XCTAssertEqual("\u{1f71}".decomposedStringWithHFSPlusMapping, "\u{03b1}\u{0301}") #expect("\u{1f71}".decomposedStringWithHFSPlusMapping == "\u{03b1}\u{0301}")
} }
func testWhitespaceTrimming() throws { @Test func trimWhitespace() throws {
let string = """ let string = """
@ -399,7 +398,7 @@ final class StringExtensionsTests: XCTestCase {
white space -> white space ->
abc abc
""" """
XCTAssertEqual(trimmed, expectedTrimmed) #expect(trimmed == expectedTrimmed)
let trimmedIgnoringEmptyLines = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoresEmptyLines: true)) let trimmedIgnoringEmptyLines = try string.trim(ranges: string.rangesOfTrailingWhitespace(ignoresEmptyLines: true))
let expectedTrimmedIgnoringEmptyLines = """ let expectedTrimmedIgnoringEmptyLines = """
@ -409,36 +408,36 @@ final class StringExtensionsTests: XCTestCase {
white space -> white space ->
abc abc
""" """
XCTAssertEqual(trimmedIgnoringEmptyLines, expectedTrimmedIgnoringEmptyLines) #expect(trimmedIgnoringEmptyLines == expectedTrimmedIgnoringEmptyLines)
} }
func testAbbreviatedMatch() throws { @Test func abbreviatedMatch() throws {
let string = "The fox jumps over the lazy dogcow." 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")) let dogcow = try #require(string.abbreviatedMatch(with: "dogcow"))
XCTAssertEqual(dogcow.score, 6) #expect(dogcow.score == 6)
XCTAssertEqual(dogcow.ranges.count, 6) #expect(dogcow.ranges.count == 6)
XCTAssertEqual(dogcow.remaining, "") #expect(dogcow.remaining.isEmpty)
let ow = try XCTUnwrap(string.abbreviatedMatch(with: "ow")) let ow = try #require(string.abbreviatedMatch(with: "ow"))
XCTAssertEqual(ow.score, 29) #expect(ow.score == 29)
XCTAssertEqual(ow.ranges.count, 2) #expect(ow.ranges.count == 2)
XCTAssertEqual(ow.remaining, "") #expect(ow.remaining.isEmpty)
let lazyTanuki = try XCTUnwrap(string.abbreviatedMatch(with: "lazy tanuki")) let lazyTanuki = try #require(string.abbreviatedMatch(with: "lazy tanuki"))
XCTAssertEqual(lazyTanuki.score, 5) #expect(lazyTanuki.score == 5)
XCTAssertEqual(lazyTanuki.ranges.count, 5) #expect(lazyTanuki.ranges.count == 5)
XCTAssertEqual(lazyTanuki.remaining, "tanuki") #expect(lazyTanuki.remaining == "tanuki")
XCTAssertNil(string.abbreviatedMatchedRanges(with: "lazy tanuki")) #expect(string.abbreviatedMatchedRanges(with: "lazy tanuki") == nil)
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count, 5) #expect(string.abbreviatedMatchedRanges(with: "lazy tanuki", incomplete: true)?.count == 5)
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy w")?.count, 6) #expect(string.abbreviatedMatchedRanges(with: "lazy w")?.count == 6)
XCTAssertEqual(string.abbreviatedMatchedRanges(with: "lazy w", incomplete: true)?.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 { func trim(ranges: [NSRange]) throws -> String {
try ranges.reversed() try ranges.reversed()
.map { try XCTUnwrap(Range($0, in: self)) } .map { try #require(Range($0, in: self)) }
.reduce(self) { $0.replacingCharacters(in: $1, with: "") } .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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,71 +24,72 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class StringIndentationTests: XCTestCase { struct StringIndentationTests {
// MARK: Indentation Style Detection Tests // MARK: Indentation Style Detection Tests
func testIndentStyleDetection() { @Test func detectIndentStyle() {
let string = "\t\tfoo\tbar" let string = "\t\tfoo\tbar"
XCTAssertNil(string.detectedIndentStyle) #expect(string.detectedIndentStyle == nil)
} }
// MARK: Indentation Style Standardization Tests // MARK: Indentation Style Standardization Tests
func testIndentStyleStandardizationToTab() { @Test func standardizeIndentStyleToTab() {
let string = " foo bar\n " let string = " foo bar\n "
// spaces to tab // spaces to tab
XCTAssertEqual(string.standardizingIndent(to: .tab, tabWidth: 2), "\t\t foo bar\n\t") #expect(string.standardizingIndent(to: .tab, tabWidth: 2) == "\t\t foo bar\n\t")
XCTAssertEqual(string.standardizingIndent(to: .space, tabWidth: 2), string) #expect(string.standardizingIndent(to: .space, tabWidth: 2) == string)
} }
func testIndentStyleStandardizationToSpace() { @Test func standardizeIndentStyleToSpace() {
let string = "\t\tfoo\tbar" let string = "\t\tfoo\tbar"
XCTAssertEqual(string.standardizingIndent(to: .space, tabWidth: 2), " foo\tbar") #expect(string.standardizingIndent(to: .space, tabWidth: 2) == " foo\tbar")
XCTAssertEqual(string.standardizingIndent(to: .tab, tabWidth: 2), string) #expect(string.standardizingIndent(to: .tab, tabWidth: 2) == string)
} }
// MARK: Other Tests // MARK: Other Tests
func testIndentLevelDetection() { @Test func detectIndentLevel() {
XCTAssertEqual(" foo".indentLevel(at: 0, tabWidth: 4), 1) #expect(" foo".indentLevel(at: 0, tabWidth: 4) == 1)
XCTAssertEqual(" foo".indentLevel(at: 4, tabWidth: 2), 2) #expect(" foo".indentLevel(at: 4, tabWidth: 2) == 2)
XCTAssertEqual("\tfoo".indentLevel(at: 4, tabWidth: 2), 1) #expect("\tfoo".indentLevel(at: 4, tabWidth: 2) == 1)
// tab-space mix // tab-space mix
XCTAssertEqual(" \t foo".indentLevel(at: 4, tabWidth: 2), 2) #expect(" \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) == 3)
// multiline // 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 " let string = " foo\n bar "
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(0..<0), tabWidth: 2)) #expect(string.rangeForSoftTabDeletion(in: NSRange(0..<0), tabWidth: 2) == nil)
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(4..<5), tabWidth: 2)) #expect(string.rangeForSoftTabDeletion(in: NSRange(4..<5), tabWidth: 2) == nil)
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(6..<6), tabWidth: 2)) #expect(string.rangeForSoftTabDeletion(in: NSRange(6..<6), tabWidth: 2) == nil)
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(5..<5), tabWidth: 2), NSRange(4..<5)) #expect(string.rangeForSoftTabDeletion(in: NSRange(5..<5), tabWidth: 2) == NSRange(4..<5))
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(4..<4), tabWidth: 2), NSRange(2..<4)) #expect(string.rangeForSoftTabDeletion(in: NSRange(4..<4), tabWidth: 2) == NSRange(2..<4))
XCTAssertNil(string.rangeForSoftTabDeletion(in: NSRange(10..<10), tabWidth: 2)) #expect(string.rangeForSoftTabDeletion(in: NSRange(10..<10), tabWidth: 2) == nil)
XCTAssertEqual(string.rangeForSoftTabDeletion(in: NSRange(11..<11), tabWidth: 2), NSRange(9..<11)) #expect(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(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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,12 +23,13 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class StringLineProcessingTests: XCTestCase { struct StringLineProcessingTests {
func testMoveLineUp() { @Test func moveLineUp() throws {
let string = """ let string = """
aa aa
@ -37,29 +38,28 @@ final class StringLineProcessingTests: XCTestCase {
d d
eee eee
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.moveLineUp(in: [NSRange(4, 1)]) info = try #require(string.moveLineUp(in: [NSRange(4, 1)]))
XCTAssertEqual(info?.strings, ["bbbb\naa\n"]) #expect(info.strings == ["bbbb\naa\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 8)]) #expect(info.ranges == [NSRange(0, 8)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1)]) #expect(info.selectedRanges == [NSRange(1, 1)])
info = string.moveLineUp(in: [NSRange(4, 1), NSRange(6, 0)]) info = try #require(string.moveLineUp(in: [NSRange(4, 1), NSRange(6, 0)]))
XCTAssertEqual(info?.strings, ["bbbb\naa\n"]) #expect(info.strings == ["bbbb\naa\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 8)]) #expect(info.ranges == [NSRange(0, 8)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1), NSRange(3, 0)]) #expect(info.selectedRanges == [NSRange(1, 1), NSRange(3, 0)])
info = string.moveLineUp(in: [NSRange(4, 1), NSRange(9, 0), NSRange(15, 1)]) info = try #require(string.moveLineUp(in: [NSRange(4, 1), NSRange(9, 0), NSRange(15, 1)]))
XCTAssertEqual(info?.strings, ["bbbb\nccc\naa\neee\nd"]) #expect(info.strings == ["bbbb\nccc\naa\neee\nd"])
XCTAssertEqual(info?.ranges, [NSRange(0, 17)]) #expect(info.ranges == [NSRange(0, 17)])
XCTAssertEqual(info?.selectedRanges, [NSRange(1, 1), NSRange(6, 0), NSRange(13, 1)]) #expect(info.selectedRanges == [NSRange(1, 1), NSRange(6, 0), NSRange(13, 1)])
info = string.moveLineUp(in: [NSRange(2, 1)]) #expect(string.moveLineUp(in: [NSRange(2, 1)]) == nil)
XCTAssertNil(info)
} }
func testMoveLineDown() { @Test func moveLineDown() throws {
let string = """ let string = """
aa aa
@ -68,77 +68,74 @@ final class StringLineProcessingTests: XCTestCase {
d d
eee eee
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.moveLineDown(in: [NSRange(4, 1)]) info = try #require(string.moveLineDown(in: [NSRange(4, 1)]))
XCTAssertEqual(info?.strings, ["aa\nccc\nbbbb\n"]) #expect(info.strings == ["aa\nccc\nbbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 12)]) #expect(info.ranges == [NSRange(0, 12)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1)]) #expect(info.selectedRanges == [NSRange(8, 1)])
info = string.moveLineDown(in: [NSRange(4, 1), NSRange(6, 0)]) info = try #require(string.moveLineDown(in: [NSRange(4, 1), NSRange(6, 0)]))
XCTAssertEqual(info?.strings, ["aa\nccc\nbbbb\n"]) #expect(info.strings == ["aa\nccc\nbbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(0, 12)]) #expect(info.ranges == [NSRange(0, 12)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1), NSRange(10, 0)]) #expect(info.selectedRanges == [NSRange(8, 1), NSRange(10, 0)])
info = string.moveLineDown(in: [NSRange(4, 1), NSRange(9, 0), NSRange(13, 1)]) info = try #require(string.moveLineDown(in: [NSRange(4, 1), NSRange(9, 0), NSRange(13, 1)]))
XCTAssertEqual(info?.strings, ["aa\neee\nbbbb\nccc\nd"]) #expect(info.strings == ["aa\neee\nbbbb\nccc\nd"])
XCTAssertEqual(info?.ranges, [NSRange(0, 17)]) #expect(info.ranges == [NSRange(0, 17)])
XCTAssertEqual(info?.selectedRanges, [NSRange(8, 1), NSRange(13, 0), NSRange(17, 1)]) #expect(info.selectedRanges == [NSRange(8, 1), NSRange(13, 0), NSRange(17, 1)])
info = string.moveLineDown(in: [NSRange(14, 1)]) #expect(string.moveLineDown(in: [NSRange(14, 1)]) == nil)
XCTAssertNil(info)
} }
func testSortLinesAscending() { @Test func sortLinesAscending() throws {
let string = """ let string = """
ccc ccc
aa aa
bbbb bbbb
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.sortLinesAscending(in: NSRange(4, 1)) #expect(string.sortLinesAscending(in: NSRange(4, 1)) == nil)
XCTAssertNil(info)
info = string.sortLinesAscending(in: string.nsRange) info = try #require(string.sortLinesAscending(in: string.nsRange))
XCTAssertEqual(info?.strings, ["aa\nbbbb\nccc"]) #expect(info.strings == ["aa\nbbbb\nccc"])
XCTAssertEqual(info?.ranges, [NSRange(0, 11)]) #expect(info.ranges == [NSRange(0, 11)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 11)]) #expect(info.selectedRanges == [NSRange(0, 11)])
info = string.sortLinesAscending(in: NSRange(2, 4)) info = try #require(string.sortLinesAscending(in: NSRange(2, 4)))
XCTAssertEqual(info?.strings, ["aa\nccc"]) #expect(info.strings == ["aa\nccc"])
XCTAssertEqual(info?.ranges, [NSRange(0, 6)]) #expect(info.ranges == [NSRange(0, 6)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 6)]) #expect(info.selectedRanges == [NSRange(0, 6)])
} }
func testReverseLines() { @Test func reverseLines() throws {
let string = """ let string = """
aa aa
bbbb bbbb
ccc ccc
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.reverseLines(in: NSRange(4, 1)) #expect(string.reverseLines(in: NSRange(4, 1)) == nil)
XCTAssertNil(info)
info = string.reverseLines(in: string.nsRange) info = try #require(string.reverseLines(in: string.nsRange))
XCTAssertEqual(info?.strings, ["ccc\nbbbb\naa"]) #expect(info.strings == ["ccc\nbbbb\naa"])
XCTAssertEqual(info?.ranges, [NSRange(0, 11)]) #expect(info.ranges == [NSRange(0, 11)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 11)]) #expect(info.selectedRanges == [NSRange(0, 11)])
info = string.reverseLines(in: NSRange(2, 4)) info = try #require(string.reverseLines(in: NSRange(2, 4)))
XCTAssertEqual(info?.strings, ["bbbb\naa"]) #expect(info.strings == ["bbbb\naa"])
XCTAssertEqual(info?.ranges, [NSRange(0, 7)]) #expect(info.ranges == [NSRange(0, 7)])
XCTAssertEqual(info?.selectedRanges, [NSRange(0, 7)]) #expect(info.selectedRanges == [NSRange(0, 7)])
} }
func testDeleteDuplicateLine() { @Test func deleteDuplicateLine() throws {
let string = """ let string = """
aa aa
@ -147,76 +144,75 @@ final class StringLineProcessingTests: XCTestCase {
ccc ccc
bbbb bbbb
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.deleteDuplicateLine(in: [NSRange(4, 1)]) #expect(string.deleteDuplicateLine(in: [NSRange(4, 1)]) == nil)
XCTAssertNil(info)
info = string.deleteDuplicateLine(in: [string.nsRange]) info = try #require(string.deleteDuplicateLine(in: [string.nsRange]))
XCTAssertEqual(info?.strings, ["", ""]) #expect(info.strings == ["", ""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4), NSRange(16, 4)]) #expect(info.ranges == [NSRange(12, 4), NSRange(16, 4)])
XCTAssertNil(info?.selectedRanges) #expect(info.selectedRanges == nil)
info = string.deleteDuplicateLine(in: [NSRange(10, 4)]) info = try #require(string.deleteDuplicateLine(in: [NSRange(10, 4)]))
XCTAssertEqual(info?.strings, [""]) #expect(info.strings == [""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4)]) #expect(info.ranges == [NSRange(12, 4)])
XCTAssertNil(info?.selectedRanges) #expect(info.selectedRanges == nil)
info = string.deleteDuplicateLine(in: [NSRange(9, 1), NSRange(11, 0), NSRange(13, 2)]) info = try #require(string.deleteDuplicateLine(in: [NSRange(9, 1), NSRange(11, 0), NSRange(13, 2)]))
XCTAssertEqual(info?.strings, [""]) #expect(info.strings == [""])
XCTAssertEqual(info?.ranges, [NSRange(12, 4)]) #expect(info.ranges == [NSRange(12, 4)])
XCTAssertNil(info?.selectedRanges) #expect(info.selectedRanges == nil)
} }
func testDuplicateLine() { @Test func duplicateLine() throws {
let string = """ let string = """
aa aa
bbbb bbbb
ccc ccc
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.duplicateLine(in: [NSRange(4, 1)], lineEnding: "\n") info = try #require(string.duplicateLine(in: [NSRange(4, 1)], lineEnding: "\n"))
XCTAssertEqual(info?.strings, ["bbbb\n"]) #expect(info.strings == ["bbbb\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0)]) #expect(info.ranges == [NSRange(3, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(9, 1)]) #expect(info.selectedRanges == [NSRange(9, 1)])
info = string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 4)], lineEnding: "\n") info = try #require(string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 4)], lineEnding: "\n"))
XCTAssertEqual(info?.strings, ["bbbb\nccc\n"]) #expect(info.strings == ["bbbb\nccc\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0)]) #expect(info.ranges == [NSRange(3, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(13, 1), NSRange(15, 4)]) #expect(info.selectedRanges == [NSRange(13, 1), NSRange(15, 4)])
info = string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)], lineEnding: "\n") info = try #require(string.duplicateLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)], lineEnding: "\n"))
XCTAssertEqual(info?.strings, ["bbbb\n", "ccc\n"]) #expect(info.strings == ["bbbb\n", "ccc\n"])
XCTAssertEqual(info?.ranges, [NSRange(3, 0), NSRange(8, 0)]) #expect(info.ranges == [NSRange(3, 0), NSRange(8, 0)])
XCTAssertEqual(info?.selectedRanges, [NSRange(9, 1), NSRange(11, 1), NSRange(19, 0)]) #expect(info.selectedRanges == [NSRange(9, 1), NSRange(11, 1), NSRange(19, 0)])
} }
func testDeleteLine() { @Test func deleteLine() throws {
let string = """ let string = """
aa aa
bbbb bbbb
ccc ccc
""" """
var info: String.EditingInfo? var info: String.EditingInfo
info = string.deleteLine(in: [NSRange(4, 1)]) info = try #require(string.deleteLine(in: [NSRange(4, 1)]))
XCTAssertEqual(info?.strings, [""]) #expect(info.strings == [""])
XCTAssertEqual(info?.ranges, [NSRange(3, 5)]) #expect(info.ranges == [NSRange(3, 5)])
XCTAssertEqual(info?.selectedRanges, [NSRange(3, 0)]) #expect(info.selectedRanges == [NSRange(3, 0)])
info = string.deleteLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)]) info = try #require(string.deleteLine(in: [NSRange(4, 1), NSRange(6, 1), NSRange(10, 0)]))
XCTAssertEqual(info?.strings, ["", ""]) #expect(info.strings == ["", ""])
XCTAssertEqual(info?.ranges, [NSRange(3, 5), NSRange(8, 3)]) #expect(info.ranges == [NSRange(3, 5), NSRange(8, 3)])
XCTAssertEqual(info?.selectedRanges, [NSRange(3, 0)]) #expect(info.selectedRanges == [NSRange(3, 0)])
} }
func testJoinLinesIn() { @Test func joinLinesIn() {
let string = """ let string = """
aa aa
@ -226,13 +222,13 @@ final class StringLineProcessingTests: XCTestCase {
""" """
let info = string.joinLines(in: [NSRange(1, 6), NSRange(10, 1)]) let info = string.joinLines(in: [NSRange(1, 6), NSRange(10, 1)])
XCTAssertEqual(info.strings, ["a bb", "c"]) #expect(info.strings == ["a bb", "c"])
XCTAssertEqual(info.ranges, [NSRange(1, 6), NSRange(10, 1)]) #expect(info.ranges == [NSRange(1, 6), NSRange(10, 1)])
XCTAssertEqual(info.selectedRanges, [NSRange(1, 4), NSRange(8, 1)]) #expect(info.selectedRanges == [NSRange(1, 4), NSRange(8, 1)])
} }
func testJoinLinesAfter() { @Test func joinLinesAfter() {
let string = """ let string = """
aa aa
@ -242,9 +238,9 @@ final class StringLineProcessingTests: XCTestCase {
""" """
let info = string.joinLines(after: [NSRange(1, 0), NSRange(10, 0), NSRange(14, 0)]) let info = string.joinLines(after: [NSRange(1, 0), NSRange(10, 0), NSRange(14, 0)])
XCTAssertEqual(info.strings, [" ", " "]) #expect(info.strings == [" ", " "])
XCTAssertEqual(info.ranges, [NSRange(2, 3), NSRange(13, 1)]) #expect(info.ranges == [NSRange(2, 3), NSRange(13, 1)])
XCTAssertNil(info.selectedRanges) #expect(info.selectedRanges == nil)
} }
} }

View File

@ -24,12 +24,13 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit
import Testing
import Combine import Combine
import Yams import Yams
@testable import CotEditor @testable import CotEditor
final class SyntaxTests: XCTestCase { final class SyntaxTests {
private let syntaxDirectoryName = "Syntaxes" private let syntaxDirectoryName = "Syntaxes"
@ -41,12 +42,10 @@ final class SyntaxTests: XCTestCase {
override func setUpWithError() throws { init() throws {
try super.setUpWithError()
let bundle = Bundle(for: type(of: self)) 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 // load syntaxes
let decoder = YAMLDecoder() let decoder = YAMLDecoder()
@ -56,129 +55,128 @@ final class SyntaxTests: XCTestCase {
dict[name] = try decoder.decode(Syntax.self, from: data) dict[name] = try decoder.decode(Syntax.self, from: data)
} }
self.htmlSyntax = try XCTUnwrap(self.syntaxes["HTML"]) self.htmlSyntax = try #require(self.syntaxes["HTML"])
XCTAssertNotNil(self.htmlSyntax)
// load test file // 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) 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 { for (name, syntax) in self.syntaxes {
let model = SyntaxObject(value: syntax) let model = SyntaxObject(value: syntax)
let errors = model.validate() let errors = model.validate()
XCTAssert(errors.isEmpty) #expect(errors.isEmpty)
for error in errors { for error in errors {
XCTFail("\(name): \(error)") Issue.record("\(name): \(error)")
} }
} }
} }
func testSanitization() { @Test func sanitize() {
for (name, syntax) in self.syntaxes { for (name, syntax) in self.syntaxes {
let sanitized = syntax.sanitized let sanitized = syntax.sanitized
XCTAssertEqual(syntax.kind, sanitized.kind) #expect(syntax.kind == sanitized.kind)
for type in SyntaxType.allCases { for type in SyntaxType.allCases {
let keyPath = Syntax.highlightKeyPath(for: type) 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") ".\(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") ".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") ".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") ".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") ".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") ".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") ".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") ".metadata of “\(name)” is not sanitized in the latest manner")
} }
} }
func testEquality() { @Test func noneSyntax() {
XCTAssertEqual(self.htmlSyntax, self.htmlSyntax)
}
func testNoneSyntax() {
let syntax = Syntax.none let syntax = Syntax.none
XCTAssertEqual(syntax.kind, .code) #expect(syntax.kind == .code)
XCTAssert(syntax.highlightParser.isEmpty) #expect(syntax.highlightParser.isEmpty)
XCTAssertNil(syntax.commentDelimiters.inline) #expect(syntax.commentDelimiters.inline == nil)
XCTAssertNil(syntax.commentDelimiters.block) #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) #expect(!syntax.highlightParser.isEmpty)
XCTAssertNil(syntax.commentDelimiters.inline) #expect(syntax.commentDelimiters.inline == nil)
XCTAssertEqual(syntax.commentDelimiters.block, Pair("<!--", "-->")) #expect(syntax.commentDelimiters.block == Pair("<!--", "-->"))
} }
func testOutlineParse() throws { @Test func parseOutline() async throws {
let syntax = try XCTUnwrap(self.htmlSyntax) let syntax = try #require(self.htmlSyntax)
let source = try XCTUnwrap(self.htmlSource) let source = try #require(self.htmlSource)
let textStorage = NSTextStorage(string: source) let textStorage = NSTextStorage(string: source)
let parser = SyntaxParser(textStorage: textStorage, syntax: syntax, name: "HTML") let parser = SyntaxParser(textStorage: textStorage, syntax: syntax, name: "HTML")
// test outline parsing with publisher // test outline parsing with publisher
let outlineParseExpectation = self.expectation(description: "didParseOutline") try await confirmation("didParseOutline") { confirm in
self.outlineParseCancellable = parser.$outlineItems self.outlineParseCancellable = parser.$outlineItems
.compactMap { $0 } // ignore the initial invocation .compactMap { $0 } // ignore the initial invocation
.receive(on: RunLoop.main) .receive(on: RunLoop.main)
.sink { outlineItems in .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] let item = outlineItems[1]
XCTAssertEqual(item.title, " h2: 🐕🐄") #expect(item.title == " h2: 🐕🐄")
XCTAssertEqual(item.range.location, 354) #expect(item.range.location == 354)
XCTAssertEqual(item.range.length, 13) #expect(item.range.length == 13)
XCTAssertTrue(item.style.isEmpty) #expect(item.style.isEmpty)
} }
parser.invalidateOutline()
self.waitForExpectations(timeout: 1) parser.invalidateOutline()
try await Task.sleep(for: .seconds(0.5))
}
} }
func testViewModelHighlightEquality() { @Test func viewModelHighlightEquality() {
let termA = SyntaxObject.Highlight(begin: "abc", end: "def") let termA = SyntaxObject.Highlight(begin: "abc", end: "def")
let termB = SyntaxObject.Highlight(begin: "abc", end: "def") let termB = SyntaxObject.Highlight(begin: "abc", end: "def")
let termC = SyntaxObject.Highlight(begin: "abc") let termC = SyntaxObject.Highlight(begin: "abc")
XCTAssertEqual(termA, termB) #expect(termA == termB)
XCTAssertNotEqual(termA, termC) #expect(termA != termC)
XCTAssertNotEqual(termA.id, termB.id) #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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -23,17 +23,18 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class TextClippingTests: XCTestCase { actor TextClippingTests {
func testReadingTextClippingFile() throws { @Test func readTextClippingFile() throws {
let bundle = Bundle(for: type(of: self)) 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) 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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,45 +24,32 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import AppKit
import Testing
@testable import CotEditor @testable import CotEditor
final class TextFindTests: XCTestCase { struct TextFindTests {
func testTextFinderActions() { @Test func finderActions() {
XCTAssertEqual(TextFinder.Action.showFindInterface.rawValue, #expect(TextFinder.Action.showFindInterface.rawValue == NSTextFinder.Action.showFindInterface.rawValue)
NSTextFinder.Action.showFindInterface.rawValue) #expect(TextFinder.Action.nextMatch.rawValue == NSTextFinder.Action.nextMatch.rawValue)
XCTAssertEqual(TextFinder.Action.nextMatch.rawValue, #expect(TextFinder.Action.previousMatch.rawValue == NSTextFinder.Action.previousMatch.rawValue)
NSTextFinder.Action.nextMatch.rawValue) #expect(TextFinder.Action.replaceAll.rawValue == NSTextFinder.Action.replaceAll.rawValue)
XCTAssertEqual(TextFinder.Action.previousMatch.rawValue, #expect(TextFinder.Action.replace.rawValue == NSTextFinder.Action.replace.rawValue)
NSTextFinder.Action.previousMatch.rawValue) #expect(TextFinder.Action.replaceAndFind.rawValue == NSTextFinder.Action.replaceAndFind.rawValue)
XCTAssertEqual(TextFinder.Action.replaceAll.rawValue, #expect(TextFinder.Action.setSearchString.rawValue == NSTextFinder.Action.setSearchString.rawValue)
NSTextFinder.Action.replaceAll.rawValue) #expect(TextFinder.Action.replaceAllInSelection.rawValue == NSTextFinder.Action.replaceAllInSelection.rawValue)
XCTAssertEqual(TextFinder.Action.replace.rawValue, #expect(TextFinder.Action.selectAll.rawValue == NSTextFinder.Action.selectAll.rawValue)
NSTextFinder.Action.replace.rawValue) #expect(TextFinder.Action.selectAllInSelection.rawValue == NSTextFinder.Action.selectAllInSelection.rawValue)
XCTAssertEqual(TextFinder.Action.replaceAndFind.rawValue, #expect(TextFinder.Action.hideFindInterface.rawValue == NSTextFinder.Action.hideFindInterface.rawValue)
NSTextFinder.Action.replaceAndFind.rawValue) #expect(TextFinder.Action.showReplaceInterface.rawValue == NSTextFinder.Action.showReplaceInterface.rawValue)
XCTAssertEqual(TextFinder.Action.setSearchString.rawValue, #expect(TextFinder.Action.showReplaceInterface.rawValue == NSTextFinder.Action.showReplaceInterface.rawValue)
NSTextFinder.Action.setSearchString.rawValue) #expect(TextFinder.Action.hideReplaceInterface.rawValue == NSTextFinder.Action.hideReplaceInterface.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)
} }
func testCaptureGroupCount() throws { @Test func countCaptureGroup() throws {
var mode: TextFind.Mode var mode: TextFind.Mode
var textFind: TextFind var textFind: TextFind
@ -70,18 +57,18 @@ final class TextFindTests: XCTestCase {
mode = .regularExpression(options: [], unescapesReplacement: false) mode = .regularExpression(options: [], unescapesReplacement: false)
textFind = try TextFind(for: "", findString: "a", mode: mode) 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) textFind = try TextFind(for: "", findString: "(?!=a)(b)(c)(?=d)", mode: mode)
XCTAssertEqual(textFind.numberOfCaptureGroups, 2) #expect(textFind.numberOfCaptureGroups == 2)
mode = .textual(options: [], fullWord: false) mode = .textual(options: [], fullWord: false)
textFind = try TextFind(for: "", findString: "(?!=a)(b)(c)(?=d)", mode: mode) 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 text = "abcdefg abcdefg ABCDEFG"
let findString = "abc" let findString = "abc"
@ -93,40 +80,40 @@ final class TextFindTests: XCTestCase {
textFind = try TextFind(for: text, findString: findString, mode: .textual(options: [], fullWord: false)) textFind = try TextFind(for: text, findString: findString, mode: .textual(options: [], fullWord: false))
matches = try textFind.matches matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: false)) result = try #require(textFind.find(in: matches, forward: true, wraps: false))
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(result.range, NSRange(location: 0, length: 3)) #expect(result.range == NSRange(location: 0, length: 3))
XCTAssertFalse(result.wrapped) #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)]) textFind = try TextFind(for: text, findString: findString, mode: .textual(options: [], fullWord: false), selectedRanges: [NSRange(location: 1, length: 0)])
matches = try textFind.matches matches = try textFind.matches
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 8, length: 3)) #expect(result.range == NSRange(location: 8, length: 3))
XCTAssertFalse(result.wrapped) #expect(!result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true)) result = try #require(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 8, length: 3)) #expect(result.range == NSRange(location: 8, length: 3))
XCTAssertTrue(result.wrapped) #expect(result.wrapped)
textFind = try TextFind(for: text, findString: findString, mode: .textual(options: .caseInsensitive, fullWord: false), selectedRanges: [NSRange(location: 1, length: 0)]) textFind = try TextFind(for: text, findString: findString, mode: .textual(options: .caseInsensitive, fullWord: false), selectedRanges: [NSRange(location: 1, length: 0)])
matches = try textFind.matches matches = try textFind.matches
XCTAssertEqual(matches.count, 3) #expect(matches.count == 3)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true)) result = try #require(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 16, length: 3)) #expect(result.range == NSRange(location: 16, length: 3))
XCTAssertTrue(result.wrapped) #expect(result.wrapped)
} }
func testFullWord() throws { @Test func fullWord() throws {
var textFind: TextFind var textFind: TextFind
var result: (range: NSRange, wrapped: Bool) var result: (range: NSRange, wrapped: Bool)
@ -135,43 +122,43 @@ final class TextFindTests: XCTestCase {
textFind = try TextFind(for: "apples apple Apple", findString: "apple", textFind = try TextFind(for: "apples apple Apple", findString: "apple",
mode: .textual(options: .caseInsensitive, fullWord: true)) mode: .textual(options: .caseInsensitive, fullWord: true))
matches = try textFind.matches matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(result.range, NSRange(location: 7, length: 5)) #expect(result.range == NSRange(location: 7, length: 5))
textFind = try TextFind(for: "apples apple Apple", findString: "apple", textFind = try TextFind(for: "apples apple Apple", findString: "apple",
mode: .textual(options: [.caseInsensitive, .literal], fullWord: true)) mode: .textual(options: [.caseInsensitive, .literal], fullWord: true))
matches = try textFind.matches matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(result.range, NSRange(location: 7, length: 5)) #expect(result.range == NSRange(location: 7, length: 5))
textFind = try TextFind(for: "Apfel Äpfel Äpfelchen", findString: "Äpfel", textFind = try TextFind(for: "Apfel Äpfel Äpfelchen", findString: "Äpfel",
mode: .textual(options: .diacriticInsensitive, fullWord: true)) mode: .textual(options: .diacriticInsensitive, fullWord: true))
matches = try textFind.matches matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(result.range, NSRange(location: 0, length: 5)) #expect(result.range == NSRange(location: 0, length: 5))
textFind = try TextFind(for: "イヌら イヌ イヌ", findString: "イヌ", textFind = try TextFind(for: "イヌら イヌ イヌ", findString: "イヌ",
mode: .textual(options: .widthInsensitive, fullWord: true)) mode: .textual(options: .widthInsensitive, fullWord: true))
matches = try textFind.matches matches = try textFind.matches
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(result.range, NSRange(location: 4, length: 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 mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: true)
let textFind = try TextFind(for: "1", findString: "1", mode: mode, selectedRanges: [NSRange(0..<1)]) let textFind = try TextFind(for: "1", findString: "1", mode: mode, selectedRanges: [NSRange(0..<1)])
let replacementResult = try XCTUnwrap(textFind.replace(with: #"foo\n1"#)) let replacementResult = try #require(textFind.replace(with: #"foo\n1"#))
XCTAssertEqual(replacementResult.value, "foo\n1") #expect(replacementResult.value == "foo\n1")
} }
func testSingleRegexFindAndReplacement() throws { @Test func findAndReplaceSingleRegex() throws {
let findString = "(?!=a)b(c)(?=d)" let findString = "(?!=a)b(c)(?=d)"
let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: true) 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)]) textFind = try TextFind(for: "abcdefg abcdefg ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 1)])
matches = try textFind.matches matches = try textFind.matches
XCTAssertEqual(matches.count, 3) #expect(matches.count == 3)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 9, length: 2)) #expect(result.range == NSRange(location: 9, length: 2))
XCTAssertFalse(result.wrapped) #expect(!result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true)) result = try #require(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 17, length: 2)) #expect(result.range == NSRange(location: 17, length: 2))
XCTAssertTrue(result.wrapped) #expect(result.wrapped)
textFind = try TextFind(for: "ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 1)]) textFind = try TextFind(for: "ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 1)])
matches = try textFind.matches matches = try textFind.matches
XCTAssertEqual(matches.count, 1) #expect(matches.count == 1)
result = try XCTUnwrap(textFind.find(in: matches, forward: true, wraps: true)) result = try #require(textFind.find(in: matches, forward: true, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 1, length: 2)) #expect(result.range == NSRange(location: 1, length: 2))
XCTAssertTrue(result.wrapped) #expect(result.wrapped)
result = try XCTUnwrap(textFind.find(in: matches, forward: false, wraps: true)) result = try #require(textFind.find(in: matches, forward: false, wraps: true))
XCTAssertEqual(result.range, NSRange(location: 1, length: 2)) #expect(result.range == NSRange(location: 1, length: 2))
XCTAssertTrue(result.wrapped) #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)]) textFind = try TextFind(for: "ABCDEFG", findString: findString, mode: mode, selectedRanges: [NSRange(location: 1, length: 2)])
let replacementResult = try XCTUnwrap(textFind.replace(with: "$1\\t")) let replacementResult = try #require(textFind.replace(with: "$1\\t"))
XCTAssertEqual(replacementResult.value, "C\t") #expect(replacementResult.value == "C\t")
XCTAssertEqual(replacementResult.range, NSRange(location: 1, length: 2)) #expect(replacementResult.range == NSRange(location: 1, length: 2))
} }
func testFindAll() throws { @Test func findAll() throws {
let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: false) let mode: TextFind.Mode = .regularExpression(options: .caseInsensitive, unescapesReplacement: false)
var textFind: TextFind var textFind: TextFind
@ -230,13 +217,13 @@ final class TextFindTests: XCTestCase {
textFind.findAll { (matchedRanges, _) in textFind.findAll { (matchedRanges, _) in
matches.append(matchedRanges) matches.append(matchedRanges)
} }
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(matches[0].count, 2) #expect(matches[0].count == 2)
XCTAssertEqual(matches[0][0], NSRange(location: 1, length: 2)) #expect(matches[0][0] == NSRange(location: 1, length: 2))
XCTAssertEqual(matches[0][1], NSRange(location: 2, length: 1)) #expect(matches[0][1] == NSRange(location: 2, length: 1))
XCTAssertEqual(matches[1].count, 2) #expect(matches[1].count == 2)
XCTAssertEqual(matches[1][0], NSRange(location: 9, length: 2)) #expect(matches[1][0] == NSRange(location: 9, length: 2))
XCTAssertEqual(matches[1][1], NSRange(location: 10, length: 1)) #expect(matches[1][1] == NSRange(location: 10, length: 1))
textFind = try TextFind(for: "abcdefg ABCDEFG", findString: "ab", mode: mode) textFind = try TextFind(for: "abcdefg ABCDEFG", findString: "ab", mode: mode)
@ -245,15 +232,15 @@ final class TextFindTests: XCTestCase {
textFind.findAll { (matchedRanges, _) in textFind.findAll { (matchedRanges, _) in
matches.append(matchedRanges) matches.append(matchedRanges)
} }
XCTAssertEqual(matches.count, 2) #expect(matches.count == 2)
XCTAssertEqual(matches[0].count, 1) #expect(matches[0].count == 1)
XCTAssertEqual(matches[0][0], NSRange(location: 0, length: 2)) #expect(matches[0][0] == NSRange(location: 0, length: 2))
XCTAssertEqual(matches[1].count, 1) #expect(matches[1].count == 1)
XCTAssertEqual(matches[1][0], NSRange(location: 8, length: 2)) #expect(matches[1][0] == NSRange(location: 8, length: 2))
} }
func testReplaceAll() throws { @Test func replaceAll() throws {
var textFind: TextFind var textFind: TextFind
var replacementItems: [TextFind.ReplacementItem] var replacementItems: [TextFind.ReplacementItem]
@ -263,10 +250,10 @@ final class TextFindTests: XCTestCase {
mode: .regularExpression(options: .caseInsensitive, unescapesReplacement: false)) mode: .regularExpression(options: .caseInsensitive, unescapesReplacement: false))
(replacementItems, selectedRanges) = textFind.replaceAll(with: "$1\\\\t") { (_, _, _) in } (replacementItems, selectedRanges) = textFind.replaceAll(with: "$1\\\\t") { (_, _, _) in }
XCTAssertEqual(replacementItems.count, 1) #expect(replacementItems.count == 1)
XCTAssertEqual(replacementItems[0].value, "ac\\tdefg AC\\tDEFG") #expect(replacementItems[0].value == "ac\\tdefg AC\\tDEFG")
XCTAssertEqual(replacementItems[0].range, NSRange(location: 0, length: 15)) #expect(replacementItems[0].range == NSRange(location: 0, length: 15))
XCTAssertNil(selectedRanges) #expect(selectedRanges == nil)
textFind = try TextFind(for: "abcdefg abcdefg abcdefg", findString: "abc", textFind = try TextFind(for: "abcdefg abcdefg abcdefg", findString: "abc",
@ -276,12 +263,12 @@ final class TextFindTests: XCTestCase {
NSRange(location: 16, length: 7)]) NSRange(location: 16, length: 7)])
(replacementItems, selectedRanges) = textFind.replaceAll(with: "_") { (_, _, _) in } (replacementItems, selectedRanges) = textFind.replaceAll(with: "_") { (_, _, _) in }
XCTAssertEqual(replacementItems.count, 2) #expect(replacementItems.count == 2)
XCTAssertEqual(replacementItems[0].value, "bcdefg _defg") #expect(replacementItems[0].value == "bcdefg _defg")
XCTAssertEqual(replacementItems[0].range, NSRange(location: 1, length: 14)) #expect(replacementItems[0].range == NSRange(location: 1, length: 14))
XCTAssertEqual(replacementItems[1].value, "_defg") #expect(replacementItems[1].value == "_defg")
XCTAssertEqual(replacementItems[1].range, NSRange(location: 16, length: 7)) #expect(replacementItems[1].range == NSRange(location: 16, length: 7))
XCTAssertEqual(selectedRanges?[0], NSRange(location: 1, length: 12)) #expect(selectedRanges?[0] == NSRange(location: 1, length: 12))
XCTAssertEqual(selectedRanges?[1], NSRange(location: 14, length: 5)) #expect(selectedRanges?[1] == NSRange(location: 14, length: 5))
} }
} }

View File

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

View File

@ -24,54 +24,55 @@
// limitations under the License. // limitations under the License.
// //
import XCTest import Foundation
import Testing
@testable import CotEditor @testable import CotEditor
final class URLExtensionsTests: XCTestCase { struct URLExtensionsTests {
func testRelativeURLCreation() { @Test func createRelativeURL() {
let url = URL(filePath: "/foo/bar/file.txt") let url = URL(filePath: "/foo/bar/file.txt")
let baseURL = URL(filePath: "/foo/buz/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 url = URL(filePath: "/file1.txt")
let baseURL = URL(filePath: "/file2.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 url = URL(filePath: "/file1.txt")
let baseURL = 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") let url = URL(filePath: "Dog/Cow/Cat/file1.txt")
XCTAssertEqual(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")
XCTAssertEqual(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")
XCTAssertEqual(url.path(relativeTo: URL(filePath: "Dog/Cow/Cat", directoryHint: .isDirectory)), "file1.txt") #expect(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: "", directoryHint: .isDirectory)) == "Dog/Cow/Cat/file1.txt")
let url2 = URL(filePath: "file1.txt") let url2 = URL(filePath: "file1.txt")
XCTAssertEqual(url2.path(relativeTo: URL(filePath: "", directoryHint: .isDirectory)), "file1.txt") #expect(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: "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 UniformTypeIdentifiers
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class UTTypeExtensionTests: XCTestCase { struct UTTypeExtensionTests {
func testFilenameExtensions() { @Test func filenameExtensions() {
XCTAssertEqual(UTType.yaml.filenameExtensions, ["yml", "yaml"]) #expect(UTType.yaml.filenameExtensions == ["yml", "yaml"])
XCTAssertEqual(UTType.svg.filenameExtensions, ["svg", "svgz"]) #expect(UTType.svg.filenameExtensions == ["svg", "svgz"])
XCTAssertEqual(UTType.mpeg2TransportStream.filenameExtensions, ["ts"]) #expect(UTType.mpeg2TransportStream.filenameExtensions == ["ts"])
XCTAssertEqual(UTType.propertyList.filenameExtensions, ["plist"]) #expect(UTType.propertyList.filenameExtensions == ["plist"])
} }
func testURLConformance() { @Test func conformURL() {
let xmlURL = URL(filePath: "foo.xml") let xmlURL = URL(filePath: "foo.xml")
XCTAssertFalse(xmlURL.conforms(to: .svg)) #expect(!xmlURL.conforms(to: .svg))
XCTAssertTrue(xmlURL.conforms(to: .xml)) #expect(xmlURL.conforms(to: .xml))
XCTAssertFalse(xmlURL.conforms(to: .plainText)) #expect(!xmlURL.conforms(to: .plainText))
let svgzURL = URL(filePath: "FOO.SVGZ") 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)) #expect(UTType.svg.conforms(to: .text))
XCTAssertTrue(UTType.svg.conforms(to: .image)) #expect(UTType.svg.conforms(to: .image))
let svgz = try XCTUnwrap(UTType(filenameExtension: "svgz")) let svgz = try #require(UTType(filenameExtension: "svgz"))
XCTAssertEqual(svgz, .svg) #expect(svgz == .svg)
XCTAssertFalse(svgz.conforms(to: .gzip)) #expect(!svgz.conforms(to: .gzip))
} }
func testPlist() throws { @Test func plist() {
XCTAssertTrue(UTType.propertyList.conforms(to: .data)) #expect(UTType.propertyList.conforms(to: .data))
XCTAssertFalse(UTType.propertyList.conforms(to: .image)) #expect(!UTType.propertyList.conforms(to: .image))
} }
func testIsPlainText() { @Test func isPlainText() {
XCTAssertTrue(UTType.propertyList.isPlainText) #expect(UTType.propertyList.isPlainText)
XCTAssertTrue(UTType.svg.isPlainText) #expect(UTType.svg.isPlainText)
XCTAssertTrue(UTType(filenameExtension: "ts")!.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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -24,110 +24,104 @@
// limitations under the License. // limitations under the License.
// //
import Foundation
import Combine import Combine
import XCTest import Testing
@testable import CotEditor @testable import CotEditor
final class UserDefaultsObservationTests: XCTestCase { struct UserDefaultsObservationTests {
func testKeyObservation() { @Test func observeKey() async {
let key = DefaultKey<Bool>("Test Key") let key = DefaultKey<Bool>("Test Key")
defer { UserDefaults.standard.restore(key: key) } defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for normal key")
UserDefaults.standard[key] = false UserDefaults.standard[key] = false
let observer = UserDefaults.standard.publisher(for: key) await confirmation("UserDefaults observation for normal key") { confirm in
.sink { value in let observer = UserDefaults.standard.publisher(for: key)
XCTAssertTrue(value) .sink { value in
XCTAssertEqual(OperationQueue.current, .main) #expect(value)
confirm()
expectation.fulfill() }
}
UserDefaults.standard[key] = true
UserDefaults.standard[key] = true
self.wait(for: [expectation], timeout: .zero) observer.cancel()
// -> Waiting with zero timeout can be failed when the closure is performed not immediately but in another runloop. UserDefaults.standard[key] = false
}
observer.cancel()
UserDefaults.standard[key] = false
} }
func testInitialEmission() { @Test func initialEmit() async {
let key = DefaultKey<Bool>("Initial Emission Test Key") let key = DefaultKey<Bool>("Initial Emission Test Key")
defer { UserDefaults.standard.restore(key: key) } defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for initial emission")
UserDefaults.standard[key] = false UserDefaults.standard[key] = false
let observer = UserDefaults.standard.publisher(for: key, initial: true) await confirmation("UserDefaults observation for initial emission") { confirm in
.sink { value in let observer = UserDefaults.standard.publisher(for: key, initial: true)
XCTAssertFalse(value) .sink { value in
expectation.fulfill() #expect(!value)
} confirm()
}
observer.cancel()
UserDefaults.standard[key] = true 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") let key = DefaultKey<String?>("Optional Test Key")
defer { UserDefaults.standard.restore(key: key) } defer { UserDefaults.standard.restore(key: key) }
XCTAssertNil(UserDefaults.standard[key]) #expect(UserDefaults.standard[key] == nil)
UserDefaults.standard[key] = "cow" 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) let observer = UserDefaults.standard.publisher(for: key)
.sink { value in .sink { value in
XCTAssertNil(value) #expect(value == nil)
expectation.fulfill() confirm()
} }
UserDefaults.standard[key] = nil UserDefaults.standard[key] = nil
self.wait(for: [expectation], timeout: .zero)
#expect(UserDefaults.standard[key] == nil)
XCTAssertNil(UserDefaults.standard[key])
observer.cancel()
observer.cancel() UserDefaults.standard[key] = "dog"
UserDefaults.standard[key] = "dog" #expect(UserDefaults.standard[key] == "dog")
XCTAssertEqual(UserDefaults.standard[key], "dog") }
} }
func testRawRepresentable() { @Test func rawRepresentable() async {
enum Clarus: Int { case dog, cow } enum Clarus: Int { case dog, cow }
let key = RawRepresentableDefaultKey<Clarus>("Raw Representable Test Key") let key = RawRepresentableDefaultKey<Clarus>("Raw Representable Test Key")
defer { UserDefaults.standard.restore(key: key) } defer { UserDefaults.standard.restore(key: key) }
let expectation = self.expectation(description: "UserDefaults observation for raw representable")
UserDefaults.standard[key] = .dog UserDefaults.standard[key] = .dog
let observer = UserDefaults.standard.publisher(for: key) await confirmation("UserDefaults observation for raw representable") { confirm in
.sink { value in let observer = UserDefaults.standard.publisher(for: key)
XCTAssertEqual(value, .cow) .sink { value in
expectation.fulfill() #expect(value == .cow)
} confirm()
}
UserDefaults.standard[key] = .cow
self.wait(for: [expectation], timeout: .zero) UserDefaults.standard[key] = .cow
observer.cancel() observer.cancel()
UserDefaults.standard[key] = .dog UserDefaults.standard[key] = .dog
XCTAssertEqual(UserDefaults.standard[key], .dog) #expect(UserDefaults.standard[key] == .dog)
}
} }
} }