chore: import upstream snapshot with attribution
This commit is contained in:
+71
@@ -0,0 +1,71 @@
|
||||
import OrcaComputerUseMacOSCore
|
||||
import XCTest
|
||||
|
||||
final class ActionArgumentValidationTests: XCTestCase {
|
||||
func testPositiveIntegerAcceptsPositiveValuesAndDefaults() {
|
||||
XCTAssertEqual(
|
||||
try ActionArgumentValidation.positiveInteger(nil, defaultValue: 1, name: "clickCount").get(),
|
||||
1
|
||||
)
|
||||
XCTAssertEqual(
|
||||
try ActionArgumentValidation.positiveInteger(2, defaultValue: 1, name: "clickCount").get(),
|
||||
2
|
||||
)
|
||||
}
|
||||
|
||||
func testPositiveIntegerRejectsZeroNegativeAndNonFiniteValues() {
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveInteger(0, defaultValue: 1, name: "clickCount")),
|
||||
"clickCount must be a positive integer"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveInteger(-1, defaultValue: 1, name: "clickCount")),
|
||||
"clickCount must be a positive integer"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveInteger(.infinity, defaultValue: 1, name: "clickCount")),
|
||||
"clickCount must be a positive integer"
|
||||
)
|
||||
}
|
||||
|
||||
func testPositiveNumberAcceptsPositiveValuesAndDefaults() {
|
||||
XCTAssertEqual(
|
||||
try ActionArgumentValidation.positiveNumber(nil, defaultValue: 1, name: "pages").get(),
|
||||
1
|
||||
)
|
||||
XCTAssertEqual(
|
||||
try ActionArgumentValidation.positiveNumber(0.5, defaultValue: 1, name: "pages").get(),
|
||||
0.5
|
||||
)
|
||||
}
|
||||
|
||||
func testPositiveNumberRejectsZeroNegativeAndNonFiniteValues() {
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveNumber(0, defaultValue: 1, name: "pages")),
|
||||
"pages must be a positive number"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveNumber(-0.5, defaultValue: 1, name: "pages")),
|
||||
"pages must be a positive number"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.positiveNumber(.nan, defaultValue: 1, name: "pages")),
|
||||
"pages must be a positive number"
|
||||
)
|
||||
}
|
||||
|
||||
func testScrollDirectionRejectsUnknownDirections() {
|
||||
XCTAssertEqual(try ActionArgumentValidation.scrollDirection("down").get(), "down")
|
||||
XCTAssertEqual(
|
||||
failureMessage(ActionArgumentValidation.scrollDirection("diagonal")),
|
||||
"unsupported scroll direction: diagonal"
|
||||
)
|
||||
}
|
||||
|
||||
private func failureMessage<T>(_ result: Result<T, ActionArgumentValidationError>) -> String? {
|
||||
if case let .failure(error) = result {
|
||||
return error.message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import XCTest
|
||||
|
||||
final class AgentEntrypointSourceSafetyTests: XCTestCase {
|
||||
func testAgentEntrypointDoesNotUnlinkCallerSuppliedPaths() throws {
|
||||
let testFile = URL(fileURLWithPath: #filePath)
|
||||
let packageRoot = testFile
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
let mainPath = packageRoot
|
||||
.appendingPathComponent("Sources")
|
||||
.appendingPathComponent("OrcaComputerUseMacOS")
|
||||
.appendingPathComponent("main.swift")
|
||||
let source = try String(contentsOf: mainPath, encoding: .utf8)
|
||||
|
||||
// Why: --agent accepts caller-supplied paths; deleting them in the
|
||||
// helper can remove user files if argument validation is bypassed.
|
||||
XCTAssertFalse(source.contains("unlink(tokenPath)"))
|
||||
XCTAssertFalse(source.contains("unlink(socketPath)"))
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import OrcaComputerUseMacOSCore
|
||||
import XCTest
|
||||
|
||||
final class ComputerSnapshotCachePolicyTests: XCTestCase {
|
||||
func testDoesNotExpireFreshSnapshotsAtTheAgeBoundary() {
|
||||
let createdAt = Date(timeIntervalSince1970: 100)
|
||||
let now = createdAt.addingTimeInterval(ComputerSnapshotCachePolicy.maxAge)
|
||||
|
||||
XCTAssertFalse(ComputerSnapshotCachePolicy.isExpired(createdAt: createdAt, now: now))
|
||||
}
|
||||
|
||||
func testExpiresSnapshotsOlderThanTheAgeLimit() {
|
||||
let createdAt = Date(timeIntervalSince1970: 100)
|
||||
let now = createdAt.addingTimeInterval(ComputerSnapshotCachePolicy.maxAge + 0.001)
|
||||
|
||||
XCTAssertTrue(ComputerSnapshotCachePolicy.isExpired(createdAt: createdAt, now: now))
|
||||
}
|
||||
|
||||
func testPrunesWhenCacheExceedsEntryLimit() {
|
||||
XCTAssertTrue(
|
||||
ComputerSnapshotCachePolicy.shouldPrune(
|
||||
entryCount: ComputerSnapshotCachePolicy.maxEntries + 1,
|
||||
createdAt: Date(),
|
||||
now: Date()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import XCTest
|
||||
@testable import OrcaComputerUseMacOSCore
|
||||
|
||||
final class KeyboardInputSafetyTests: XCTestCase {
|
||||
func testSyntheticInputRequiresFocusedTargetWindow() {
|
||||
let cases: [(focused: Bool, restoreWindow: Bool, expectedFailure: KeyboardInputSafety.FocusFailure?)] = [
|
||||
(focused: true, restoreWindow: false, expectedFailure: nil),
|
||||
(focused: true, restoreWindow: true, expectedFailure: nil),
|
||||
(focused: false, restoreWindow: false, expectedFailure: .targetNotFocused),
|
||||
(focused: false, restoreWindow: true, expectedFailure: .targetNotFocusedAfterRestore),
|
||||
]
|
||||
|
||||
for testCase in cases {
|
||||
XCTAssertEqual(
|
||||
KeyboardInputSafety.syntheticInputFocusFailure(
|
||||
targetWindowFocused: testCase.focused,
|
||||
restoreWindowRequested: testCase.restoreWindow
|
||||
),
|
||||
testCase.expectedFailure
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import XCTest
|
||||
@testable import OrcaComputerUseMacOSCore
|
||||
|
||||
final class NumericArgumentParsingTests: XCTestCase {
|
||||
func testConvertsIntegralValues() {
|
||||
XCTAssertEqual(boundedInteger(5.0, as: Int.self), 5)
|
||||
XCTAssertEqual(boundedInteger(0.0, as: Int.self), 0)
|
||||
XCTAssertEqual(boundedInteger(-3.0, as: Int.self), -3)
|
||||
}
|
||||
|
||||
func testTruncatesTowardZeroLikeIntInit() {
|
||||
XCTAssertEqual(boundedInteger(5.9, as: Int.self), 5)
|
||||
XCTAssertEqual(boundedInteger(-5.9, as: Int.self), -5)
|
||||
}
|
||||
|
||||
// The crash this guards against: Int(1e300) traps because the value is
|
||||
// finite but outside Int's range. A malformed request must not take the
|
||||
// agent down.
|
||||
func testReturnsNilForOutOfRangeValues() {
|
||||
XCTAssertNil(boundedInteger(1e300, as: Int.self))
|
||||
XCTAssertNil(boundedInteger(-1e300, as: Int.self))
|
||||
}
|
||||
|
||||
func testReturnsNilForNonFiniteValues() {
|
||||
XCTAssertNil(boundedInteger(.nan, as: Int.self))
|
||||
XCTAssertNil(boundedInteger(.infinity, as: Int.self))
|
||||
XCTAssertNil(boundedInteger(-.infinity, as: Int.self))
|
||||
}
|
||||
|
||||
func testHonorsUnsignedDestinationBounds() {
|
||||
XCTAssertEqual(boundedInteger(4.0, as: UInt32.self), 4)
|
||||
XCTAssertNil(boundedInteger(-1.0, as: UInt32.self))
|
||||
XCTAssertNil(boundedInteger(1e300, as: UInt32.self))
|
||||
}
|
||||
|
||||
func testHonorsSignedFixedWidthDestinationBounds() {
|
||||
XCTAssertEqual(boundedInteger(Double(Int32.max), as: Int32.self), Int32.max)
|
||||
XCTAssertNil(boundedInteger(Double(Int32.max) + 1, as: Int32.self))
|
||||
XCTAssertNil(boundedInteger(Double(Int32.min) - 1, as: Int32.self))
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
import XCTest
|
||||
@testable import OrcaComputerUseMacOSCore
|
||||
|
||||
final class SnapshotRenderingTests: XCTestCase {
|
||||
func testSkipsUnsupportedAdvertisedAttributes() {
|
||||
let advertised: Set<String> = ["AXRole", "AXChildren"]
|
||||
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.supportsAttribute("AXRole", advertisedAttributes: advertised))
|
||||
XCTAssertFalse(SnapshotRenderHeuristics.supportsAttribute("AXTitle", advertisedAttributes: advertised))
|
||||
}
|
||||
|
||||
func testUnknownAttributeAdvertisementsStayPermissive() {
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.supportsAttribute("AXTitle", advertisedAttributes: nil))
|
||||
}
|
||||
|
||||
func testSecureTextMetadataOnlyProbesTextLikeRoles() {
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldProbeSecureTextMetadata(role: "AXTextField"))
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldProbeSecureTextMetadata(role: "AXSearchField"))
|
||||
XCTAssertFalse(SnapshotRenderHeuristics.shouldProbeSecureTextMetadata(role: "AXGroup"))
|
||||
XCTAssertFalse(SnapshotRenderHeuristics.shouldProbeSecureTextMetadata(role: "AXButton"))
|
||||
}
|
||||
|
||||
func testElidesAnonymousWrappers() {
|
||||
let node = SnapshotRenderNode(role: "AXGroup", childCount: 1)
|
||||
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldElide(node))
|
||||
}
|
||||
|
||||
func testPreservesWebAreaContainersWithMultipleChildren() {
|
||||
let node = SnapshotRenderNode(role: "AXGroup", childCount: 3, webAreaDepth: 1)
|
||||
|
||||
XCTAssertFalse(SnapshotRenderHeuristics.shouldElide(node))
|
||||
}
|
||||
|
||||
func testRendersMarkdownLinksAndSuppressesTheirChildren() {
|
||||
let node = SnapshotRenderNode(role: "AXLink", linkText: "Skip [main]", url: "https://example.com/path")
|
||||
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.line(index: 7, node: node), "7 link [Skip \\[main\\]](https://example.com/path)")
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldSuppressChildren(node))
|
||||
}
|
||||
|
||||
func testSuppressesChildrenForNamedCompactControls() {
|
||||
let button = SnapshotRenderNode(role: "AXButton", roleDescription: "button", label: "Install GitHub", childCount: 1)
|
||||
let heading = SnapshotRenderNode(role: "AXHeading", roleDescription: "heading", label: "Repository navigation", value: "2", childCount: 1)
|
||||
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldSuppressChildren(button))
|
||||
XCTAssertTrue(SnapshotRenderHeuristics.shouldSuppressChildren(heading))
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.line(index: 5, node: heading), "5 heading Repository navigation")
|
||||
}
|
||||
|
||||
func testKeepsChildrenForRowsWithNestedControls() {
|
||||
let row = SnapshotRenderNode(role: "AXRow", roleDescription: "row", childCount: 3, rowSummary: "Liked Songs")
|
||||
|
||||
XCTAssertFalse(SnapshotRenderHeuristics.shouldSuppressChildren(row))
|
||||
}
|
||||
|
||||
func testFiltersNoisyActionsAndFormatsSecondaryActions() {
|
||||
let node = SnapshotRenderNode(
|
||||
role: "AXWindow",
|
||||
title: "Document",
|
||||
rawActions: ["AXPress", "AXShowMenu", "AXScrollToVisible", "AXZoomWindow"]
|
||||
)
|
||||
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.meaningfulActions(node.rawActions, role: node.role), ["AXZoomWindow"])
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.line(index: 1, node: node), "1 window Document, Secondary Actions: zoom the window")
|
||||
}
|
||||
|
||||
func testSuppressesHorizontalScrollWhenVerticalScrollExists() {
|
||||
let node = SnapshotRenderNode(
|
||||
role: "AXScrollArea",
|
||||
rawActions: ["AXScrollUpByPage", "AXScrollDownByPage", "AXScrollLeftByPage", "AXScrollRightByPage"]
|
||||
)
|
||||
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.meaningfulActions(node.rawActions, role: node.role), ["AXScrollUpByPage", "AXScrollDownByPage"])
|
||||
}
|
||||
|
||||
func testTextFieldsKeepDistinctValueAndPlaceholder() {
|
||||
let node = SnapshotRenderNode(
|
||||
role: "AXTextField",
|
||||
roleDescription: "text field",
|
||||
label: "Address",
|
||||
value: "https://example.com",
|
||||
placeholder: "Search"
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
SnapshotRenderHeuristics.line(index: 3, node: node),
|
||||
"3 text field Address, Value: https://example.com, Placeholder: Search"
|
||||
)
|
||||
}
|
||||
|
||||
func testStaticTextUsesCompactValueFormatting() {
|
||||
let node = SnapshotRenderNode(role: "AXStaticText", roleDescription: "text", value: "Home")
|
||||
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.line(index: 4, node: node), "4 text Home")
|
||||
}
|
||||
|
||||
func testRowSummaryBecomesName() {
|
||||
let node = SnapshotRenderNode(role: "AXRow", roleDescription: "row", rowSummary: "General Settings Enabled")
|
||||
|
||||
XCTAssertEqual(SnapshotRenderHeuristics.line(index: 9, node: node), "9 row General Settings Enabled")
|
||||
}
|
||||
|
||||
func testCompactsLargeBrowserTabStripsToSelectedTab() {
|
||||
let parent = SnapshotRenderNode(role: "AXScrollArea", roleDescription: "tab bar")
|
||||
let children = (0..<12).map { index in
|
||||
SnapshotRenderNode(
|
||||
role: "AXRadioButton",
|
||||
roleDescription: "tab",
|
||||
title: "Tab \(index)",
|
||||
traits: index == 7 ? ["selected"] : []
|
||||
)
|
||||
}
|
||||
|
||||
let compaction = SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children)
|
||||
|
||||
XCTAssertEqual(compaction, SnapshotTabStripCompaction(retainedIndexes: [7], omittedCount: 11))
|
||||
}
|
||||
|
||||
func testInfersLargeBrowserTabStripFromScrollAreaChildren() {
|
||||
let parent = SnapshotRenderNode(role: "AXScrollArea", roleDescription: "scroll area")
|
||||
let children = (0..<12).map { index in
|
||||
SnapshotRenderNode(
|
||||
role: "AXRadioButton",
|
||||
roleDescription: "tab",
|
||||
title: "Tab \(index)",
|
||||
traits: index == 11 ? ["selected"] : []
|
||||
)
|
||||
}
|
||||
|
||||
let compaction = SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children)
|
||||
|
||||
XCTAssertEqual(compaction, SnapshotTabStripCompaction(retainedIndexes: [11], omittedCount: 11))
|
||||
}
|
||||
|
||||
func testUsesOneValueAsSelectedBrowserTabFallback() {
|
||||
let parent = SnapshotRenderNode(role: "AXScrollArea", roleDescription: "scroll area")
|
||||
let children = (0..<12).map { index in
|
||||
SnapshotRenderNode(
|
||||
role: "AXRadioButton",
|
||||
roleDescription: "tab",
|
||||
title: "Tab \(index)",
|
||||
value: index == 4 ? "1" : "0"
|
||||
)
|
||||
}
|
||||
|
||||
let compaction = SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children)
|
||||
|
||||
XCTAssertEqual(compaction, SnapshotTabStripCompaction(retainedIndexes: [4], omittedCount: 11))
|
||||
}
|
||||
|
||||
func testKeepsLargeTabCollectionsWithoutActiveTabExpanded() {
|
||||
let parent = SnapshotRenderNode(role: "AXGroup")
|
||||
let children = (0..<12).map { index in
|
||||
SnapshotRenderNode(role: "AXTab", title: "Tab \(index)", value: "0")
|
||||
}
|
||||
|
||||
XCTAssertNil(SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children))
|
||||
}
|
||||
|
||||
func testKeepsLargeNonBrowserTabCollectionsExpanded() {
|
||||
let parent = SnapshotRenderNode(role: "AXGroup")
|
||||
let children = (0..<12).map { index in
|
||||
SnapshotRenderNode(
|
||||
role: "AXRadioButton",
|
||||
roleDescription: "tab",
|
||||
title: "Pane \(index)",
|
||||
traits: index == 2 ? ["selected"] : []
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertNil(SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children))
|
||||
}
|
||||
|
||||
func testKeepsSmallTabGroupsExpanded() {
|
||||
let parent = SnapshotRenderNode(role: "AXTabGroup", roleDescription: "tab group")
|
||||
let children = (0..<3).map { index in
|
||||
SnapshotRenderNode(
|
||||
role: "AXRadioButton",
|
||||
roleDescription: "tab",
|
||||
title: "Pane \(index)",
|
||||
traits: index == 1 ? ["selected"] : []
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertNil(SnapshotRenderHeuristics.tabStripCompaction(parent: parent, children: children))
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import Darwin
|
||||
import XCTest
|
||||
@testable import OrcaComputerUseMacOSCore
|
||||
|
||||
final class UnixSocketPathSafetyTests: XCTestCase {
|
||||
func testOnlyUnixSocketModesAreAccepted() {
|
||||
XCTAssertTrue(UnixSocketPathSafety.isSocketMode(mode_t(S_IFSOCK | 0o600)))
|
||||
XCTAssertFalse(UnixSocketPathSafety.isSocketMode(mode_t(S_IFREG | 0o600)))
|
||||
XCTAssertFalse(UnixSocketPathSafety.isSocketMode(mode_t(S_IFDIR | 0o700)))
|
||||
XCTAssertFalse(UnixSocketPathSafety.isSocketMode(mode_t(S_IFLNK | 0o777)))
|
||||
}
|
||||
|
||||
func testRejectsOnlyNonSocketPathsAfterAddressInUseBindFailure() {
|
||||
XCTAssertTrue(
|
||||
UnixSocketPathSafety.shouldRejectExistingPathAfterBindFailure(
|
||||
bindErrno: EADDRINUSE,
|
||||
existingMode: mode_t(S_IFREG | 0o600)
|
||||
)
|
||||
)
|
||||
XCTAssertFalse(
|
||||
UnixSocketPathSafety.shouldRejectExistingPathAfterBindFailure(
|
||||
bindErrno: EADDRINUSE,
|
||||
existingMode: mode_t(S_IFSOCK | 0o600)
|
||||
)
|
||||
)
|
||||
XCTAssertFalse(
|
||||
UnixSocketPathSafety.shouldRejectExistingPathAfterBindFailure(
|
||||
bindErrno: EACCES,
|
||||
existingMode: mode_t(S_IFREG | 0o600)
|
||||
)
|
||||
)
|
||||
XCTAssertFalse(
|
||||
UnixSocketPathSafety.shouldRejectExistingPathAfterBindFailure(
|
||||
bindErrno: EADDRINUSE,
|
||||
existingMode: nil
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user