mirror of
https://github.com/softinio/Fishee.git
synced 2025-02-22 13:36:04 -08:00
Initial Version (work in progress)
This commit is contained in:
parent
93069902ad
commit
9dd3399d5c
11 changed files with 544 additions and 57 deletions
63
.gitignore
vendored
63
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
# ---> Swift
|
||||
# Xcode
|
||||
#
|
||||
|
@ -5,60 +6,8 @@
|
|||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
|
||||
## App packaging
|
||||
*.ipa
|
||||
*.dSYM.zip
|
||||
*.dSYM
|
||||
|
||||
## Playgrounds
|
||||
timeline.xctimeline
|
||||
playground.xcworkspace
|
||||
|
||||
# Swift Package Manager
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
|
||||
# Packages/
|
||||
# Package.pins
|
||||
# Package.resolved
|
||||
# *.xcodeproj
|
||||
#
|
||||
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
|
||||
# hence it is not needed unless you have added a package configuration file to your project
|
||||
# .swiftpm
|
||||
|
||||
.build/
|
||||
|
||||
# CocoaPods
|
||||
#
|
||||
# We recommend against adding the Pods directory to your .gitignore. However
|
||||
# you should judge for yourself, the pros and cons are mentioned at:
|
||||
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
||||
#
|
||||
# Pods/
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from the Xcode workspace
|
||||
# *.xcworkspace
|
||||
|
||||
# Carthage
|
||||
#
|
||||
# Add this line if you want to avoid checking in source code from Carthage dependencies.
|
||||
# Carthage/Checkouts
|
||||
|
||||
Carthage/Build/
|
||||
|
||||
# fastlane
|
||||
#
|
||||
# It is recommended to not store the screenshots in the git repo.
|
||||
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
|
||||
# For more information about the recommended setup visit:
|
||||
# https://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots/**/*.png
|
||||
fastlane/test_output
|
||||
|
||||
DerivedData/
|
||||
.swiftpm
|
||||
.netrc
|
||||
.vscode/
|
||||
**/*.swp
|
||||
|
|
33
Package.swift
Normal file
33
Package.swift
Normal file
|
@ -0,0 +1,33 @@
|
|||
// swift-tools-version: 6.0
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Fishee",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMajor(from: "1.5.0")),
|
||||
.package(url: "https://github.com/duckdb/duckdb-swift", .upToNextMinor(from: .init(1, 1, 0))),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||
// Targets can depend on other targets in this package and products from dependencies.
|
||||
.executableTarget(
|
||||
name: "Fishee",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "DuckDB", package: "duckdb-swift"),
|
||||
],
|
||||
path: "Sources"
|
||||
),
|
||||
.testTarget(
|
||||
name: "FisheeTests",
|
||||
dependencies: ["Fishee"],
|
||||
path: "Tests",
|
||||
resources: [
|
||||
.copy("Resources/fish_history_test.txt"),
|
||||
.copy("Resources/fish_history_test_2.txt"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
41
Sources/Algebra.swift
Normal file
41
Sources/Algebra.swift
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FishHistoryEntry: Equatable {
|
||||
let cmd: String
|
||||
let when: Int
|
||||
let paths: [String]
|
||||
|
||||
func getDate() -> Date {
|
||||
Date(timeIntervalSince1970: TimeInterval(when))
|
||||
}
|
||||
|
||||
func writeEntry() -> [String] {
|
||||
var output: [String] = []
|
||||
|
||||
output.append("- cmd: \(cmd)")
|
||||
output.append(" when: \(when)")
|
||||
|
||||
if !paths.isEmpty {
|
||||
output.append(" paths:")
|
||||
paths.forEach { output.append(" - \(String($0))") }
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
38
Sources/FileHelpers.swift
Normal file
38
Sources/FileHelpers.swift
Normal file
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
func getPath(_ pathStr: String) -> URL? {
|
||||
let userHomeDirectory = FileManager.default.homeDirectoryForCurrentUser.path
|
||||
var filePath: String = pathStr
|
||||
|
||||
if pathStr.hasPrefix("~") {
|
||||
filePath = (pathStr as NSString).expandingTildeInPath
|
||||
}
|
||||
|
||||
if pathStr.hasPrefix("$HOME") {
|
||||
filePath = filePath.replacingOccurrences(of: "$HOME", with: userHomeDirectory)
|
||||
}
|
||||
|
||||
if !FileManager.default.fileExists(atPath: filePath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return URL(fileURLWithPath: filePath)
|
||||
}
|
103
Sources/Fishee.swift
Normal file
103
Sources/Fishee.swift
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
|
||||
import ArgumentParser
|
||||
import Foundation
|
||||
|
||||
let DEFAULT_FISH_HISTORY_LOCATION: String = "~/.local/share/fish/fish_history"
|
||||
|
||||
@main
|
||||
struct Fishee: ParsableCommand {
|
||||
@Option(name: [.short, .customLong("history-file")], help: "Location of your fish history file. Will default to ~/.local/share/fish/fish_history")
|
||||
var fishHistoryLocationStr: String?
|
||||
|
||||
@Option(name: .shortAndLong, help: "File path to file to merge with history file.")
|
||||
var mergeFile: String?
|
||||
|
||||
@Option(
|
||||
name: [.short, .customLong("output-file")],
|
||||
help: "File to write to. Default: same as current history file."
|
||||
)
|
||||
var writeFileStr: String?
|
||||
|
||||
@Flag(
|
||||
name: .shortAndLong,
|
||||
help: "Dry run. Will only print to the console without actually modifying the history file."
|
||||
)
|
||||
var dryRun: Bool = false
|
||||
|
||||
@Flag(
|
||||
name: .shortAndLong,
|
||||
help: "Remove duplicates from combined history. Default: false"
|
||||
)
|
||||
var removeDuplicates: Bool = false
|
||||
|
||||
@Flag(
|
||||
name: .shortAndLong,
|
||||
inversion: .prefixedNo,
|
||||
help: "Backup fish history file given before writing."
|
||||
)
|
||||
var backup: Bool = true
|
||||
|
||||
var fishHistoryLocation: URL? {
|
||||
let pathStr = fishHistoryLocationStr ?? DEFAULT_FISH_HISTORY_LOCATION
|
||||
return getPath(pathStr)
|
||||
}
|
||||
|
||||
var writeFileLocation: URL? {
|
||||
let pathStr = writeFileStr ?? DEFAULT_FISH_HISTORY_LOCATION
|
||||
return getPath(pathStr)
|
||||
}
|
||||
|
||||
public func run() throws {
|
||||
let mergeFileLocation = mergeFile.flatMap { getPath($0) }
|
||||
let finalHistory: [FishHistoryEntry] = switch (fishHistoryLocation, mergeFileLocation) {
|
||||
case let (fishHistoryLocation?, mergeFileLocation?):
|
||||
{
|
||||
let currentHistory = parseFishHistory(from: fishHistoryLocation.path) ?? []
|
||||
let toMergeHistory = parseFishHistory(from: mergeFileLocation.path) ?? []
|
||||
return mergeFishHistory(currentHistory, toMergeHistory, removeDuplicates: removeDuplicates)
|
||||
}()
|
||||
case let (fishHistoryLocation?, nil):
|
||||
parseFishHistory(from: fishHistoryLocation.path) ?? []
|
||||
default:
|
||||
[]
|
||||
}
|
||||
|
||||
if dryRun {
|
||||
finalHistory.forEach { print("\($0.writeEntry().joined(separator: "\n"))") }
|
||||
}
|
||||
else {
|
||||
if let writePath = writeFileLocation?.path {
|
||||
let result = writeFishHistory(
|
||||
to: writePath,
|
||||
history: finalHistory,
|
||||
historyFileLocation: fishHistoryLocation?.path,
|
||||
backup: backup
|
||||
)
|
||||
if result {
|
||||
print("Succussfully updated \(writePath)")
|
||||
}
|
||||
else {
|
||||
print("Failed to update \(writePath)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
131
Sources/Parser.swift
Normal file
131
Sources/Parser.swift
Normal file
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
func backupHistory(_ path: String) -> Bool {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
guard fileManager.fileExists(atPath: path) else {
|
||||
print("File does not exist at path: \(path)")
|
||||
return false
|
||||
}
|
||||
|
||||
let fileURL = URL(fileURLWithPath: path)
|
||||
let fileExtension = fileURL.pathExtension
|
||||
let fileNameWithoutExtension = fileURL.deletingPathExtension().lastPathComponent
|
||||
let directory = fileURL.deletingLastPathComponent()
|
||||
|
||||
let newFileName = "\(fileNameWithoutExtension)_copy"
|
||||
let newFileURL = directory.appendingPathComponent(newFileName).appendingPathExtension(fileExtension)
|
||||
|
||||
do {
|
||||
try fileManager.copyItem(at: fileURL, to: newFileURL)
|
||||
print("File duplicated successfully to: \(newFileURL.path)")
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func writeFishHistory(to path: String, history: [FishHistoryEntry], historyFileLocation: String?, backup: Bool = true) -> Bool {
|
||||
var output = ""
|
||||
|
||||
if backup {
|
||||
if let backupFile = historyFileLocation {
|
||||
let result = backupHistory(backupFile)
|
||||
if !result {
|
||||
print("Failed to backup \(backupFile) so aborting!")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
history.forEach { output += $0.writeEntry().joined(separator: "\n") + "\n" }
|
||||
|
||||
if !output.isEmpty {
|
||||
do {
|
||||
try output.write(toFile: path, atomically: true, encoding: .utf8)
|
||||
print("Successfully wrote merged history to \(path)")
|
||||
return true
|
||||
} catch {
|
||||
print("Error writing merged history: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
else {
|
||||
print("Nothing to write to \(path)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func parseFishHistory(from filePath: String) -> [FishHistoryEntry]? {
|
||||
guard let fileContents = try? String(contentsOfFile: filePath) else {
|
||||
print("Failed to open file.")
|
||||
return nil
|
||||
}
|
||||
|
||||
let lines = fileContents.split(separator: "\n").map { String($0).trimmingCharacters(in: .whitespaces) }
|
||||
|
||||
let initialState: (entries: [FishHistoryEntry], currentCmd: String?, currentWhen: Int?, currentPaths: [String]) = ([], nil, nil, [])
|
||||
|
||||
let result = lines.reduce(into: initialState) { state, line in
|
||||
if line.starts(with: "- cmd:") {
|
||||
if let cmd = state.currentCmd, let when = state.currentWhen {
|
||||
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: state.currentPaths)
|
||||
state.entries.append(entry)
|
||||
state.currentPaths = []
|
||||
}
|
||||
state.currentCmd = String(line.dropFirst("- cmd:".count).trimmingCharacters(in: .whitespaces))
|
||||
} else if line.starts(with: "when:") {
|
||||
if let whenValue = Int(line.dropFirst("when:".count).trimmingCharacters(in: .whitespaces)) {
|
||||
state.currentWhen = whenValue
|
||||
}
|
||||
} else if line.starts(with: "paths:") {
|
||||
return
|
||||
} else if line.starts(with: "- ") {
|
||||
let path = String(line.dropFirst("- ".count).trimmingCharacters(in: .whitespaces))
|
||||
state.currentPaths.append(path)
|
||||
}
|
||||
}
|
||||
|
||||
if let cmd = result.currentCmd, let when = result.currentWhen {
|
||||
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: result.currentPaths)
|
||||
return result.entries + [entry]
|
||||
}
|
||||
|
||||
return result.entries
|
||||
}
|
||||
|
||||
func mergeFishHistory(_ left: [FishHistoryEntry], _ right: [FishHistoryEntry], removeDuplicates: Bool = false) -> [FishHistoryEntry] {
|
||||
|
||||
let merged = left + right
|
||||
|
||||
if removeDuplicates {
|
||||
let finalList = merged.reduce(into: [String:FishHistoryEntry]()) { result, entry in
|
||||
if result[entry.cmd] == nil {
|
||||
result[entry.cmd] = entry
|
||||
}
|
||||
}
|
||||
return Array(finalList.values)
|
||||
} else {
|
||||
return merged
|
||||
}
|
||||
|
||||
}
|
42
Tests/AlgebraTest.swift
Normal file
42
Tests/AlgebraTest.swift
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Fishee
|
||||
|
||||
@Suite
|
||||
final class AlgebraTests {
|
||||
let historyItem = FishHistoryEntry(cmd: "cd Projects/Fishee/", when: 1727545693, paths: ["Projects/Fishee/"])
|
||||
|
||||
@Test func dateFromHistoryTest() {
|
||||
let gotDate = historyItem.getDate()
|
||||
#expect(gotDate == Date(timeIntervalSince1970: 1727545693))
|
||||
}
|
||||
|
||||
@Test func writeEntryTest() {
|
||||
let entry = historyItem.writeEntry()
|
||||
#expect(entry.count > 0)
|
||||
let expectedEntry = """
|
||||
- cmd: cd Projects/Fishee/
|
||||
when: 1727545693
|
||||
paths:
|
||||
- Projects/Fishee/
|
||||
"""
|
||||
#expect(entry.joined(separator: "\n") == expectedEntry)
|
||||
}
|
||||
|
||||
}
|
47
Tests/FileHelpersTests.swift
Normal file
47
Tests/FileHelpersTests.swift
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Fishee
|
||||
|
||||
|
||||
final class FileHelpersTests {
|
||||
let filePath = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("myfile.txt")
|
||||
|
||||
init() {
|
||||
try? "this is a test".write(
|
||||
to: filePath,
|
||||
atomically: true,
|
||||
encoding: .utf8
|
||||
)
|
||||
}
|
||||
|
||||
deinit {
|
||||
try? FileManager.default.removeItem(at: filePath)
|
||||
}
|
||||
|
||||
@Test(arguments: [
|
||||
"$HOME/myfile.txt",
|
||||
"~/myfile.txt",
|
||||
"\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt"
|
||||
])
|
||||
func getPathTest(testPath: String) {
|
||||
let path = getPath(testPath)
|
||||
let expected = URL(fileURLWithPath: "\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt")
|
||||
#expect(path == expected)
|
||||
}
|
||||
}
|
90
Tests/ParserTests.swift
Normal file
90
Tests/ParserTests.swift
Normal file
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Copyright © 2024 Salar Rahmanian.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import Fishee
|
||||
|
||||
@Suite
|
||||
final class ParserTests {
|
||||
let fishHistoryFile = Bundle.module.path(forResource: "fish_history_test", ofType: "txt")
|
||||
let historyItem = FishHistoryEntry(cmd: "cd Projects/Fishee/", when: 1727545693, paths: ["Projects/Fishee/"])
|
||||
let historyItem2 = FishHistoryEntry(cmd: "swift package tools-version", when: 1727545709, paths: [])
|
||||
let filePathforWriteTest = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0].appendingPathComponent(
|
||||
"myfile.txt"
|
||||
)
|
||||
let filePathforFileBackupTest = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)[0].appendingPathComponent(
|
||||
"myfile.txt_copy"
|
||||
)
|
||||
|
||||
deinit {
|
||||
if FileManager.default.fileExists(atPath: filePathforWriteTest.path) {
|
||||
_ = try? FileManager.default.removeItem(at: filePathforWriteTest)
|
||||
}
|
||||
if FileManager.default.fileExists(atPath: filePathforFileBackupTest.path) {
|
||||
_ = try? FileManager.default.removeItem(at: filePathforFileBackupTest)
|
||||
}
|
||||
}
|
||||
|
||||
@Test func parseFishHistoryTest() {
|
||||
#expect(fishHistoryFile != nil)
|
||||
let fishHistory = parseFishHistory(from: fishHistoryFile!)
|
||||
#expect(fishHistory!.count > 0)
|
||||
let expectedHistory = [historyItem, historyItem2]
|
||||
#expect(fishHistory == expectedHistory)
|
||||
}
|
||||
|
||||
@Test func writeFishHistoryTest() {
|
||||
let written = writeFishHistory(
|
||||
to: filePathforWriteTest.path,
|
||||
history: [historyItem],
|
||||
historyFileLocation: fishHistoryFile
|
||||
)
|
||||
#expect(written)
|
||||
|
||||
let fileContent = try? String(contentsOf: filePathforWriteTest, encoding: .utf8)
|
||||
let expectedEntry = """
|
||||
- cmd: cd Projects/Fishee/
|
||||
when: 1727545693
|
||||
paths:
|
||||
- Projects/Fishee/
|
||||
|
||||
"""
|
||||
#expect(fileContent == expectedEntry)
|
||||
#expect(FileManager.default.fileExists(atPath: filePathforFileBackupTest.path))
|
||||
}
|
||||
|
||||
@Test func mergeFishHistoryTest() {
|
||||
let merged = mergeFishHistory([historyItem], [historyItem2])
|
||||
#expect(merged.count == 2)
|
||||
#expect(merged.contains(historyItem))
|
||||
#expect(merged.contains(historyItem2))
|
||||
}
|
||||
|
||||
@Test func mergeFishHistoryWithDuplicateTest() {
|
||||
let merged = mergeFishHistory([historyItem], [historyItem, historyItem2])
|
||||
#expect(merged.count == 3)
|
||||
#expect(merged.contains(historyItem))
|
||||
#expect(merged.contains(historyItem2))
|
||||
}
|
||||
|
||||
@Test func mergeFishHistoryRemoveDuplicateTest() {
|
||||
let merged = mergeFishHistory([historyItem], [historyItem, historyItem2], removeDuplicates: true)
|
||||
#expect(merged.count == 2)
|
||||
#expect(merged.contains(historyItem))
|
||||
#expect(merged.contains(historyItem2))
|
||||
}
|
||||
}
|
7
Tests/Resources/fish_history_test.txt
Normal file
7
Tests/Resources/fish_history_test.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
- cmd: cd Projects/Fishee/
|
||||
when: 1727545693
|
||||
paths:
|
||||
- Projects/Fishee/
|
||||
- cmd: swift package tools-version
|
||||
when: 1727545709
|
||||
|
6
Tests/Resources/fish_history_test_2.txt
Normal file
6
Tests/Resources/fish_history_test_2.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
- cmd: gh pr create
|
||||
when: 1727024167
|
||||
- cmd: nix flake update
|
||||
when: 1727140536
|
||||
- cmd: swift package tools-version
|
||||
when: 1727545709
|
Loading…
Reference in a new issue