mirror of
https://github.com/nicklockwood/SwiftFormat.git
synced 2026-05-17 10:30:35 +00:00
306 lines
11 KiB
Swift
306 lines
11 KiB
Swift
//
|
|
// SwiftFormatTests.swift
|
|
// SwiftFormat
|
|
//
|
|
// Created by Nick Lockwood on 28/08/2016.
|
|
// Copyright 2016 Nick Lockwood
|
|
//
|
|
// Distributed under the permissive MIT license
|
|
// Get the latest version from here:
|
|
//
|
|
// https://github.com/nicklockwood/SwiftFormat
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import SwiftFormat
|
|
|
|
final class SwiftFormatTests: XCTestCase {
|
|
// MARK: enumerateFiles
|
|
|
|
func testInputFileMatchesOutputFileForNilOutput() {
|
|
var files = [URL]()
|
|
let inputURL = URL(fileURLWithPath: #file)
|
|
let errors = enumerateFiles(withInputURLs: [inputURL]) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
XCTAssertEqual(inputURL, URL(fileURLWithPath: #file))
|
|
return { files.append(inputURL) }
|
|
}
|
|
XCTAssertEqual(errors.count, 0)
|
|
XCTAssertEqual(files.count, 1)
|
|
}
|
|
|
|
func testInputFileMatchesOutputFileForSameOutput() {
|
|
var files = [URL]()
|
|
let inputURL = URL(fileURLWithPath: #file)
|
|
let errors = enumerateFiles(withInputURLs: [inputURL], outputURL: inputURL) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
XCTAssertEqual(inputURL, URL(fileURLWithPath: #file))
|
|
return { files.append(inputURL) }
|
|
}
|
|
XCTAssertEqual(errors.count, 0)
|
|
XCTAssertEqual(files.count, 1)
|
|
}
|
|
|
|
func testInputFilesMatchOutputFilesForNilOutput() {
|
|
var files = [URL]()
|
|
let inputURL = URL(fileURLWithPath: #file).deletingLastPathComponent().deletingLastPathComponent()
|
|
let errors = enumerateFiles(withInputURLs: [inputURL]) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
return { files.append(inputURL) }
|
|
}
|
|
XCTAssertEqual(errors.count, 0)
|
|
XCTAssertGreaterThanOrEqual(files.count, 180)
|
|
}
|
|
|
|
func testInputFilesMatchOutputFilesForSameOutput() {
|
|
var files = [URL]()
|
|
let inputURL = URL(fileURLWithPath: #file).deletingLastPathComponent().deletingLastPathComponent()
|
|
let errors = enumerateFiles(withInputURLs: [inputURL], outputURL: inputURL) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
return { files.append(inputURL) }
|
|
}
|
|
XCTAssertEqual(errors.count, 0)
|
|
XCTAssertGreaterThanOrEqual(files.count, 180)
|
|
}
|
|
|
|
func testInputFileNotEnumeratedWhenExcluded() {
|
|
var files = [URL]()
|
|
let currentFile = URL(fileURLWithPath: #file)
|
|
let options = Options(fileOptions: FileOptions(excludedGlobs: [
|
|
Glob.path(currentFile.deletingLastPathComponent().path),
|
|
]))
|
|
let inputURL = currentFile.deletingLastPathComponent().deletingLastPathComponent()
|
|
let errors = enumerateFiles(withInputURLs: [inputURL], outputURL: inputURL, options: options) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
return { files.append(inputURL) }
|
|
}
|
|
|
|
var allFiles = [URL]()
|
|
let allFilesInputURL = URL(fileURLWithPath: #file).deletingLastPathComponent().deletingLastPathComponent()
|
|
_ = enumerateFiles(withInputURLs: [allFilesInputURL], outputURL: allFilesInputURL) { inputURL, outputURL, _ in
|
|
XCTAssertEqual(inputURL, outputURL)
|
|
return { allFiles.append(inputURL) }
|
|
}
|
|
|
|
XCTAssertEqual(errors.count, 0)
|
|
XCTAssertLessThan(files.count, allFiles.count)
|
|
}
|
|
|
|
// MARK: format function
|
|
|
|
func testFormatReturnsInputWithNoRules() {
|
|
let input = "foo () "
|
|
XCTAssertEqual(try format(input, rules: []).output, input)
|
|
}
|
|
|
|
func testFormatUsesDefaultRulesIfNoneSpecified() {
|
|
let input = "foo () "
|
|
let output = "foo()\n"
|
|
XCTAssertEqual(try format(input).output, output)
|
|
}
|
|
|
|
// MARK: lint function
|
|
|
|
func testLintReturnsNoChangesWithNoRules() {
|
|
let input = "foo () "
|
|
XCTAssertEqual(try lint(input, rules: []), [])
|
|
}
|
|
|
|
func testLintWithDefaultRules() {
|
|
let input = "foo () "
|
|
XCTAssertEqual(try lint(input), [
|
|
.init(line: 1, rule: .linebreakAtEndOfFile, filePath: nil, isMove: false),
|
|
.init(line: 1, rule: .spaceAroundParens, filePath: nil, isMove: false),
|
|
.init(line: 1, rule: .trailingSpace, filePath: nil, isMove: false),
|
|
])
|
|
}
|
|
|
|
func testLintConsecutiveBlankLinesAtEndOfFile() {
|
|
let input = "foo\n\n"
|
|
XCTAssertEqual(try lint(input), [
|
|
.init(line: 2, rule: .consecutiveBlankLines, filePath: nil, isMove: false),
|
|
])
|
|
}
|
|
|
|
// MARK: fragments
|
|
|
|
func testFormattingFailsForFragment() {
|
|
let input = "foo () {"
|
|
XCTAssertThrowsError(try format(input, rules: [])) {
|
|
XCTAssertEqual("\($0)", "Unexpected end of file at 1:9")
|
|
}
|
|
}
|
|
|
|
func testFormattingSucceedsForFragmentWithOption() {
|
|
let input = "foo () {"
|
|
let options = FormatOptions(fragment: true)
|
|
XCTAssertEqual(try format(input, rules: [], options: options).output, input)
|
|
}
|
|
|
|
// MARK: conflict markers
|
|
|
|
func testFormattingFailsForConflict() {
|
|
let input = "foo () {\n<<<<<< old\n bar()\n======\n baz()\n>>>>>> new\n}"
|
|
XCTAssertThrowsError(try format(input, rules: [])) {
|
|
XCTAssertEqual("\($0)", "Found conflict marker <<<<<< at 2:1")
|
|
}
|
|
}
|
|
|
|
func testFormattingSucceedsForConflictWithOption() {
|
|
let input = "foo () {\n<<<<<< old\n bar()\n======\n baz()\n>>>>>> new\n}"
|
|
let options = FormatOptions(ignoreConflictMarkers: true)
|
|
XCTAssertEqual(try format(input, rules: [], options: options).output, input)
|
|
}
|
|
|
|
// MARK: empty file
|
|
|
|
func testNoTimeoutForEmptyFile() {
|
|
let input = ""
|
|
XCTAssertEqual(try format(input).output, input)
|
|
}
|
|
|
|
// MARK: offsetForToken
|
|
|
|
func testOffsetForToken() {
|
|
let tokens = tokenize("// a comment\n let foo = 5\n")
|
|
let offset = offsetForToken(at: 7, in: tokens, tabWidth: 1)
|
|
XCTAssertEqual(offset, SourceOffset(line: 2, column: 9))
|
|
}
|
|
|
|
func testOffsetForTokenWithTabs() {
|
|
let tokens = tokenize("// a comment\n\tlet foo = 5\n")
|
|
let offset = offsetForToken(at: 7, in: tokens, tabWidth: 2)
|
|
XCTAssertEqual(offset, SourceOffset(line: 2, column: 7))
|
|
}
|
|
|
|
// MARK: tokenIndex for offset
|
|
|
|
func testTokenIndexForOffset() {
|
|
let tokens = tokenize("// a comment\n let foo = 5\n")
|
|
let offset = SourceOffset(line: 2, column: 9)
|
|
XCTAssertEqual(tokenIndex(for: offset, in: tokens, tabWidth: 1), 7)
|
|
}
|
|
|
|
func testTokenIndexForOffsetWithTabs() {
|
|
let tokens = tokenize("// a comment\n\tlet foo = 5\n")
|
|
let offset = SourceOffset(line: 2, column: 7)
|
|
XCTAssertEqual(tokenIndex(for: offset, in: tokens, tabWidth: 2), 7)
|
|
}
|
|
|
|
func testTokenIndexForLastLine() {
|
|
let tokens = tokenize("""
|
|
let foo = 5
|
|
let bar = 6
|
|
""")
|
|
let offset = SourceOffset(line: 2, column: 0)
|
|
XCTAssertEqual(tokenIndex(for: offset, in: tokens, tabWidth: 1), 8)
|
|
}
|
|
|
|
func testTokenIndexPastEndOfFile() {
|
|
let tokens = tokenize("""
|
|
let foo = 5
|
|
let bar = 6
|
|
""")
|
|
let offset = SourceOffset(line: 3, column: 0)
|
|
XCTAssertEqual(tokenIndex(for: offset, in: tokens, tabWidth: 1), 15)
|
|
}
|
|
|
|
func testTokenIndexForBlankLastLine() {
|
|
let tokens = tokenize("""
|
|
let foo = 5
|
|
let bar = 6
|
|
|
|
""")
|
|
let offset = SourceOffset(line: 3, column: 0)
|
|
XCTAssertEqual(tokenIndex(for: offset, in: tokens, tabWidth: 1), 16)
|
|
}
|
|
|
|
// MARK: tokenRange
|
|
|
|
func testTokenRange() {
|
|
let tokens = tokenize("// a comment\n let foo = 5\n")
|
|
XCTAssertEqual(tokenRange(forLineRange: 1 ... 1, in: tokens), 0 ..< 3)
|
|
}
|
|
|
|
// MARK: newOffset
|
|
|
|
func testNewOffsetsForUnchangedPosition() {
|
|
let tokens = tokenize("foo\nbar\nbaz")
|
|
let offset1 = SourceOffset(line: 1, column: 1)
|
|
let offset2 = SourceOffset(line: 2, column: 1)
|
|
let offset3 = SourceOffset(line: 3, column: 1)
|
|
XCTAssertEqual(newOffset(for: offset1, in: tokens, tabWidth: 1), offset1)
|
|
XCTAssertEqual(newOffset(for: offset2, in: tokens, tabWidth: 1), offset2)
|
|
XCTAssertEqual(newOffset(for: offset3, in: tokens, tabWidth: 1), offset3)
|
|
}
|
|
|
|
func testNewOffsetsForRemovedLine() throws {
|
|
let input = tokenize("foo\nbar\n\n\nbaz\nquux")
|
|
let offset1 = SourceOffset(line: 1, column: 1)
|
|
let offset2 = SourceOffset(line: 2, column: 1)
|
|
let offset3 = SourceOffset(line: 5, column: 1)
|
|
let offset4 = SourceOffset(line: 6, column: 1)
|
|
let output = try format(input, rules: [.consecutiveBlankLines]).tokens
|
|
let expected3 = SourceOffset(line: 4, column: 1)
|
|
let expected4 = SourceOffset(line: 5, column: 1)
|
|
XCTAssertEqual(newOffset(for: offset1, in: output, tabWidth: 1), offset1)
|
|
XCTAssertEqual(newOffset(for: offset2, in: output, tabWidth: 1), offset2)
|
|
XCTAssertEqual(newOffset(for: offset3, in: output, tabWidth: 1), expected3)
|
|
XCTAssertEqual(newOffset(for: offset4, in: output, tabWidth: 1), expected4)
|
|
}
|
|
|
|
func testNewOffsetsForEmptyOutput() {
|
|
let offset = SourceOffset(line: 1, column: 1)
|
|
XCTAssertEqual(newOffset(for: offset, in: [], tabWidth: 1), offset)
|
|
}
|
|
|
|
// MARK: expand path
|
|
|
|
func testExpandPathWithRelativePath() {
|
|
XCTAssertEqual(
|
|
expandPath("relpath/to/file.swift", in: "/dir").path,
|
|
"/dir/relpath/to/file.swift"
|
|
)
|
|
}
|
|
|
|
func testExpandPathWithFullPath() {
|
|
XCTAssertEqual(
|
|
expandPath("/full/path/to/file.swift", in: "/dir").path,
|
|
"/full/path/to/file.swift"
|
|
)
|
|
}
|
|
|
|
func testExpandPathWithUserPath() {
|
|
XCTAssertEqual(
|
|
expandPath("~/file.swift", in: "/dir").path,
|
|
NSString(string: "~/file.swift").expandingTildeInPath
|
|
)
|
|
}
|
|
|
|
// MARK: shared option inference
|
|
|
|
func testLinebreakInferredForBlankLinesBetweenScopes() {
|
|
let input = "class Foo {\r func bar() {\r }\r func baz() {\r }\r}"
|
|
let output = "class Foo {\r func bar() {\r }\r\r func baz() {\r }\r}"
|
|
XCTAssertEqual(try format(input, rules: [.blankLinesBetweenScopes]).output, output)
|
|
}
|
|
}
|