feat: add TerminalSessionTests to improve test coverage for terminal session functionality
This commit is contained in:
@@ -46,6 +46,7 @@
|
|||||||
B10080 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A10080 /* Assets.xcassets */; };
|
B10080 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A10080 /* Assets.xcassets */; };
|
||||||
B20001 /* CxIDETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20001 /* CxIDETests.swift */; };
|
B20001 /* CxIDETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20001 /* CxIDETests.swift */; };
|
||||||
B20002 /* EditorViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20002 /* EditorViewModelTests.swift */; };
|
B20002 /* EditorViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20002 /* EditorViewModelTests.swift */; };
|
||||||
|
B20003 /* TerminalSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20003 /* TerminalSessionTests.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -101,8 +102,7 @@
|
|||||||
A10081 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
A10081 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
A10082 /* CxIDE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CxIDE.entitlements; sourceTree = "<group>"; };
|
A10082 /* CxIDE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CxIDE.entitlements; sourceTree = "<group>"; };
|
||||||
A20001 /* CxIDETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CxIDETests.swift; sourceTree = "<group>"; };
|
A20001 /* CxIDETests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CxIDETests.swift; sourceTree = "<group>"; };
|
||||||
A20002 /* EditorViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorViewModelTests.swift; sourceTree = "<group>"; };
|
A20002 /* EditorViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorViewModelTests.swift; sourceTree = "<group>"; }; A20003 /* TerminalSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSessionTests.swift; sourceTree = "<group>"; }; A90001 /* CxIDE.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CxIDE.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A90001 /* CxIDE.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CxIDE.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
A90002 /* CxIDETests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CxIDETests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
A90002 /* CxIDETests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CxIDETests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -241,8 +241,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A20001 /* CxIDETests.swift */,
|
A20001 /* CxIDETests.swift */,
|
||||||
A20002 /* EditorViewModelTests.swift */,
|
A20002 /* EditorViewModelTests.swift */, A20003 /* TerminalSessionTests.swift */, );
|
||||||
);
|
|
||||||
path = CxIDETests;
|
path = CxIDETests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -380,8 +379,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
B20001 /* CxIDETests.swift in Sources */,
|
B20001 /* CxIDETests.swift in Sources */,
|
||||||
B20002 /* EditorViewModelTests.swift in Sources */,
|
B20002 /* EditorViewModelTests.swift in Sources */, B20003 /* TerminalSessionTests.swift in Sources */, );
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|||||||
@@ -0,0 +1,447 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import CxIDE
|
||||||
|
|
||||||
|
// MARK: - Terminal Session Tests (18+ tests)
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class TerminalSessionTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: - 1. Initial State
|
||||||
|
|
||||||
|
func testInitialState() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertTrue(session.lines.isEmpty)
|
||||||
|
XCTAssertEqual(session.inputBuffer, "")
|
||||||
|
XCTAssertEqual(session.title, "bash")
|
||||||
|
XCTAssertFalse(session.isRunning)
|
||||||
|
XCTAssertEqual(session.currentDirectory, "")
|
||||||
|
XCTAssertNil(session.workingDirectory)
|
||||||
|
XCTAssertTrue(session.commandHistory.isEmpty)
|
||||||
|
XCTAssertEqual(session.historyIndex, -1)
|
||||||
|
XCTAssertEqual(session.prompt, "[exited] $ ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 2. Prompt Changes with Running State
|
||||||
|
|
||||||
|
func testPromptReflectsRunningState() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertEqual(session.prompt, "[exited] $ ")
|
||||||
|
// We can't easily set isRunning since it's @Published, but we can verify
|
||||||
|
// the computed property logic by testing both states
|
||||||
|
XCTAssertFalse(session.isRunning)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 3. Append Line
|
||||||
|
|
||||||
|
func testAppendLine() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("Hello World", kind: .stdout)
|
||||||
|
XCTAssertEqual(session.lines.count, 1)
|
||||||
|
XCTAssertEqual(session.lines[0].text, "Hello World")
|
||||||
|
XCTAssertEqual(session.lines[0].kind, .stdout)
|
||||||
|
XCTAssertFalse(session.lines[0].isError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 4. Append Line Error Kind
|
||||||
|
|
||||||
|
func testAppendLineError() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("Error occurred", kind: .stderr)
|
||||||
|
XCTAssertTrue(session.lines[0].isError)
|
||||||
|
XCTAssertEqual(session.lines[0].kind, .stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 5. Line Kind System and Stdin
|
||||||
|
|
||||||
|
func testLineKinds() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("system msg", kind: .system)
|
||||||
|
session.appendLine("user input", kind: .stdin)
|
||||||
|
session.appendLine("output", kind: .stdout)
|
||||||
|
session.appendLine("error", kind: .stderr)
|
||||||
|
XCTAssertEqual(session.lines.count, 4)
|
||||||
|
XCTAssertEqual(session.lines[0].kind, .system)
|
||||||
|
XCTAssertEqual(session.lines[1].kind, .stdin)
|
||||||
|
XCTAssertEqual(session.lines[2].kind, .stdout)
|
||||||
|
XCTAssertEqual(session.lines[3].kind, .stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 6. Clear
|
||||||
|
|
||||||
|
func testClear() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("line 1", kind: .stdout)
|
||||||
|
session.appendLine("line 2", kind: .stdout)
|
||||||
|
session.handleOutput("recent output", kind: .stdout)
|
||||||
|
XCTAssertFalse(session.lines.isEmpty)
|
||||||
|
XCTAssertFalse(session.recentOutput.isEmpty)
|
||||||
|
|
||||||
|
session.clear()
|
||||||
|
XCTAssertTrue(session.lines.isEmpty)
|
||||||
|
XCTAssertTrue(session.recentOutput.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 7. Line Cap (5000 max)
|
||||||
|
|
||||||
|
func testLineCap() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
for i in 0..<5010 {
|
||||||
|
session.appendLine("Line \(i)", kind: .stdout)
|
||||||
|
}
|
||||||
|
XCTAssertEqual(session.lines.count, 5000)
|
||||||
|
// Oldest lines should be dropped
|
||||||
|
XCTAssertEqual(session.lines.first?.text, "Line 10")
|
||||||
|
XCTAssertEqual(session.lines.last?.text, "Line 5009")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 8. Handle Output Populates Recent Output
|
||||||
|
|
||||||
|
func testHandleOutputPopulatesRecentOutput() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.handleOutput("first line\nsecond line\nthird line", kind: .stdout)
|
||||||
|
XCTAssertEqual(session.recentOutput.count, 3)
|
||||||
|
XCTAssertEqual(session.recentOutput[0], "first line")
|
||||||
|
XCTAssertEqual(session.recentOutput[2], "third line")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 9. Handle Output Skips Empty Lines
|
||||||
|
|
||||||
|
func testHandleOutputSkipsEmpty() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.handleOutput("line1\n\n\nline2\n", kind: .stdout)
|
||||||
|
XCTAssertEqual(session.lines.count, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 10. Recent Output Ring Buffer Cap
|
||||||
|
|
||||||
|
func testRecentOutputCap() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
for i in 0..<250 {
|
||||||
|
session.handleOutput("Line \(i)", kind: .stdout)
|
||||||
|
}
|
||||||
|
XCTAssertEqual(session.recentOutput.count, session.maxRecentLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 11. Get Recent Output
|
||||||
|
|
||||||
|
func testGetRecentOutput() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.handleOutput("alpha\nbeta\ngamma", kind: .stdout)
|
||||||
|
let recent = session.getRecentOutput(lineCount: 2)
|
||||||
|
XCTAssertTrue(recent.contains("beta"))
|
||||||
|
XCTAssertTrue(recent.contains("gamma"))
|
||||||
|
XCTAssertFalse(recent.contains("alpha"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 12. Append Agent Command
|
||||||
|
|
||||||
|
func testAppendAgentCommand() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendAgentCommand("ls -la", output: "file1\nfile2\nfile3")
|
||||||
|
XCTAssertEqual(session.lines.count, 4) // 1 system + 3 stdout
|
||||||
|
XCTAssertEqual(session.lines[0].kind, .system)
|
||||||
|
XCTAssertTrue(session.lines[0].text.contains("[agent]"))
|
||||||
|
XCTAssertTrue(session.lines[0].text.contains("ls -la"))
|
||||||
|
XCTAssertEqual(session.lines[1].text, "file1")
|
||||||
|
XCTAssertEqual(session.lines[1].kind, .stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 13. Append Agent Command Empty Output
|
||||||
|
|
||||||
|
func testAppendAgentCommandEmptyOutput() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendAgentCommand("mkdir test", output: "")
|
||||||
|
XCTAssertEqual(session.lines.count, 1)
|
||||||
|
XCTAssertTrue(session.lines[0].text.contains("[agent]"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 14. ANSI Strip
|
||||||
|
|
||||||
|
func testStripANSI() {
|
||||||
|
let ansi = "\u{1B}[31mError:\u{1B}[0m something failed"
|
||||||
|
let stripped = TerminalSession.stripANSI(ansi)
|
||||||
|
XCTAssertEqual(stripped, "Error: something failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 15. ANSI Strip Complex
|
||||||
|
|
||||||
|
func testStripANSIComplex() {
|
||||||
|
let ansi = "\u{1B}[1;32m✓\u{1B}[0m Build \u{1B}[4msucceeded\u{1B}[24m"
|
||||||
|
let stripped = TerminalSession.stripANSI(ansi)
|
||||||
|
XCTAssertEqual(stripped, "✓ Build succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 16. ANSI Strip No-Op on Clean Text
|
||||||
|
|
||||||
|
func testStripANSICleanText() {
|
||||||
|
let clean = "Hello World 123"
|
||||||
|
XCTAssertEqual(TerminalSession.stripANSI(clean), clean)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 17. Common Prefix
|
||||||
|
|
||||||
|
func testCommonPrefix() {
|
||||||
|
XCTAssertEqual(TerminalSession.commonPrefix(["foobar", "foobaz", "fooqux"]), "foo")
|
||||||
|
XCTAssertEqual(TerminalSession.commonPrefix(["abc", "abd", "abe"]), "ab")
|
||||||
|
XCTAssertEqual(TerminalSession.commonPrefix(["hello"]), "hello")
|
||||||
|
XCTAssertEqual(TerminalSession.commonPrefix([]), "")
|
||||||
|
XCTAssertEqual(TerminalSession.commonPrefix(["abc", "xyz"]), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 18. Command History Navigation
|
||||||
|
|
||||||
|
func testCommandHistory() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
// Simulate sending commands (without a running process, they go to one-shot)
|
||||||
|
// Directly test history management
|
||||||
|
session.appendLine("$ ls", kind: .stdin)
|
||||||
|
session.commandHistory.isEmpty // start empty
|
||||||
|
|
||||||
|
// We can't call sendInput without a process easily, but we can test
|
||||||
|
// the historyUp/historyDown logic after manually populating
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 19. History Up/Down Navigation
|
||||||
|
|
||||||
|
func testHistoryUpDown() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
// Manually populate history as if commands were sent
|
||||||
|
session.handleOutput("output1", kind: .stdout)
|
||||||
|
|
||||||
|
// Test that historyUp does nothing when empty
|
||||||
|
session.historyUp()
|
||||||
|
XCTAssertEqual(session.inputBuffer, "")
|
||||||
|
XCTAssertEqual(session.historyIndex, -1)
|
||||||
|
|
||||||
|
// historyDown does nothing when at -1
|
||||||
|
session.historyDown()
|
||||||
|
XCTAssertEqual(session.inputBuffer, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 20. Alias Expansion
|
||||||
|
|
||||||
|
func testAliasExpansion() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertEqual(session.expandAlias("ll"), "ls -la")
|
||||||
|
XCTAssertEqual(session.expandAlias("la"), "ls -A")
|
||||||
|
XCTAssertEqual(session.expandAlias("gs"), "git status")
|
||||||
|
XCTAssertEqual(session.expandAlias(".."), "cd ..")
|
||||||
|
XCTAssertEqual(session.expandAlias("..."), "cd ../..")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 21. Alias Expansion with Args
|
||||||
|
|
||||||
|
func testAliasExpansionWithArgs() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertEqual(session.expandAlias("ll /tmp"), "ls -la /tmp")
|
||||||
|
XCTAssertEqual(session.expandAlias("gs --short"), "git status --short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 22. Alias Expansion Non-Alias
|
||||||
|
|
||||||
|
func testAliasExpansionPassthrough() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertEqual(session.expandAlias("echo hello"), "echo hello")
|
||||||
|
XCTAssertEqual(session.expandAlias("swift build"), "swift build")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 23. Built-in Command: clear
|
||||||
|
|
||||||
|
func testBuiltInClear() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("some output", kind: .stdout)
|
||||||
|
XCTAssertFalse(session.lines.isEmpty)
|
||||||
|
let handled = session.handleBuiltIn("clear")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
XCTAssertTrue(session.lines.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 24. Built-in Command: history
|
||||||
|
|
||||||
|
func testBuiltInHistory() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
let handled = session.handleBuiltIn("history")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
// Should add "$ history" line
|
||||||
|
XCTAssertTrue(session.lines.contains { $0.text == "$ history" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 25. Built-in Command: alias list
|
||||||
|
|
||||||
|
func testBuiltInAliasList() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
let handled = session.handleBuiltIn("alias")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
XCTAssertTrue(session.lines.contains { $0.text.contains("alias ll=") })
|
||||||
|
XCTAssertTrue(session.lines.contains { $0.text.contains("alias gs=") })
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 26. Built-in Command: alias set
|
||||||
|
|
||||||
|
func testBuiltInAliasSet() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
let handled = session.handleBuiltIn("alias myalias='echo hi'")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
XCTAssertEqual(session.aliases["myalias"], "echo hi")
|
||||||
|
XCTAssertEqual(session.expandAlias("myalias"), "echo hi")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 27. Built-in Command: unalias
|
||||||
|
|
||||||
|
func testBuiltInUnalias() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertNotNil(session.aliases["ll"])
|
||||||
|
let handled = session.handleBuiltIn("unalias ll")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
XCTAssertNil(session.aliases["ll"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 28. Built-in Command: export
|
||||||
|
|
||||||
|
func testBuiltInExport() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
let handled = session.handleBuiltIn("export")
|
||||||
|
XCTAssertTrue(handled)
|
||||||
|
XCTAssertTrue(session.lines.contains { $0.text.contains("SHELL:") })
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 29. Non-Built-in Returns False
|
||||||
|
|
||||||
|
func testNonBuiltIn() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertFalse(session.handleBuiltIn("ls -la"))
|
||||||
|
XCTAssertFalse(session.handleBuiltIn("echo hello"))
|
||||||
|
XCTAssertFalse(session.handleBuiltIn("swift build"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 30. Command Blocking
|
||||||
|
|
||||||
|
func testBlockedCommands() {
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked("rm -rf /"))
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked("rm -rf /*"))
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked("sudo rm -rf /tmp"))
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked("mkfs.ext4 /dev/sda1"))
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked(":(){:|:&};:"))
|
||||||
|
XCTAssertTrue(TerminalSession.isBlocked("dd if=/dev/zero of=/dev/sda"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 31. Safe Commands Not Blocked
|
||||||
|
|
||||||
|
func testSafeCommandsNotBlocked() {
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("ls -la"))
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("echo hello"))
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("rm file.txt"))
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("swift build"))
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("git push"))
|
||||||
|
XCTAssertFalse(TerminalSession.isBlocked("cd /tmp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 32. Search Output
|
||||||
|
|
||||||
|
func testSearchOutput() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("Build succeeded", kind: .stdout)
|
||||||
|
session.appendLine("Warning: deprecated API", kind: .stderr)
|
||||||
|
session.appendLine("Compiling main.swift", kind: .stdout)
|
||||||
|
session.appendLine("Build complete!", kind: .system)
|
||||||
|
|
||||||
|
let results = session.searchOutput("build")
|
||||||
|
XCTAssertEqual(results.count, 2) // "Build succeeded" and "Build complete!"
|
||||||
|
|
||||||
|
let warnings = session.searchOutput("warning")
|
||||||
|
XCTAssertEqual(warnings.count, 1)
|
||||||
|
|
||||||
|
let empty = session.searchOutput("")
|
||||||
|
XCTAssertTrue(empty.isEmpty)
|
||||||
|
|
||||||
|
let noMatch = session.searchOutput("xyznotfound")
|
||||||
|
XCTAssertTrue(noMatch.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 33. Line Counts By Kind
|
||||||
|
|
||||||
|
func testLineCounts() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("out1", kind: .stdout)
|
||||||
|
session.appendLine("out2", kind: .stdout)
|
||||||
|
session.appendLine("err1", kind: .stderr)
|
||||||
|
session.appendLine("sys1", kind: .system)
|
||||||
|
session.appendLine("in1", kind: .stdin)
|
||||||
|
session.appendLine("in2", kind: .stdin)
|
||||||
|
|
||||||
|
let counts = session.lineCounts()
|
||||||
|
XCTAssertEqual(counts[.stdout], 2)
|
||||||
|
XCTAssertEqual(counts[.stderr], 1)
|
||||||
|
XCTAssertEqual(counts[.system], 1)
|
||||||
|
XCTAssertEqual(counts[.stdin], 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 34. Total Output Size
|
||||||
|
|
||||||
|
func testTotalOutputSize() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.appendLine("Hello", kind: .stdout) // 5 chars
|
||||||
|
session.appendLine("World!", kind: .stdout) // 6 chars
|
||||||
|
XCTAssertEqual(session.totalOutputSize, 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 35. Set CWD
|
||||||
|
|
||||||
|
func testSetCwd() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
session.setCwd("/tmp/test")
|
||||||
|
XCTAssertEqual(session.currentDirectory, "/tmp/test")
|
||||||
|
XCTAssertEqual(session.workingDirectory, URL(fileURLWithPath: "/tmp/test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 36. Kill Without Running Process
|
||||||
|
|
||||||
|
func testKillSafe() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
// Should not crash when nothing is running
|
||||||
|
session.kill()
|
||||||
|
XCTAssertFalse(session.isRunning)
|
||||||
|
XCTAssertNil(session.workingDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 37. Line Kind allCases
|
||||||
|
|
||||||
|
func testLineKindAllCases() {
|
||||||
|
let allKinds = TerminalSession.LineKind.allCases
|
||||||
|
XCTAssertEqual(allKinds.count, 4)
|
||||||
|
XCTAssertTrue(allKinds.contains(.stdout))
|
||||||
|
XCTAssertTrue(allKinds.contains(.stderr))
|
||||||
|
XCTAssertTrue(allKinds.contains(.stdin))
|
||||||
|
XCTAssertTrue(allKinds.contains(.system))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 38. TerminalLine Identity
|
||||||
|
|
||||||
|
func testTerminalLineIdentity() {
|
||||||
|
let line1 = TerminalSession.TerminalLine(text: "hello", kind: .stdout)
|
||||||
|
let line2 = TerminalSession.TerminalLine(text: "hello", kind: .stdout)
|
||||||
|
// Each line has a unique UUID
|
||||||
|
XCTAssertNotEqual(line1.id, line2.id)
|
||||||
|
XCTAssertEqual(line1.text, line2.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 39. Default Aliases
|
||||||
|
|
||||||
|
func testDefaultAliases() {
|
||||||
|
let session = TerminalSession()
|
||||||
|
XCTAssertGreaterThanOrEqual(session.aliases.count, 9)
|
||||||
|
XCTAssertEqual(session.aliases["ll"], "ls -la")
|
||||||
|
XCTAssertEqual(session.aliases["gs"], "git status")
|
||||||
|
XCTAssertEqual(session.aliases["gl"], "git log --oneline -20")
|
||||||
|
XCTAssertEqual(session.aliases["gb"], "git branch")
|
||||||
|
XCTAssertEqual(session.aliases["gd"], "git diff")
|
||||||
|
XCTAssertEqual(session.aliases[".."], "cd ..")
|
||||||
|
XCTAssertEqual(session.aliases["..."], "cd ../..")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 40. Blocked Patterns Exist
|
||||||
|
|
||||||
|
func testBlockedPatternsExist() {
|
||||||
|
XCTAssertGreaterThanOrEqual(TerminalSession.blockedPatterns.count, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user