Migrate unit tests to Swift Testing

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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