Skip to content

Commit

Permalink
feat: add action factory tests and Device discovery tests
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Sep 10, 2024
1 parent 547310c commit d9790ea
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 1 deletion.
8 changes: 8 additions & 0 deletions MiniSim.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
76BF0AF02C9061E8003BE568 /* DeviceDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0AEF2C9061E8003BE568 /* DeviceDiscoveryService.swift */; };
76BF0AF22C907033003BE568 /* DeviceServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0AF12C907032003BE568 /* DeviceServiceFactory.swift */; };
76BF0AF42C90A74E003BE568 /* AppleUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0AF32C90A74E003BE568 /* AppleUtilsTests.swift */; };
76BF0AF62C90AB03003BE568 /* DeviceDiscoveryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0AF52C90AB03003BE568 /* DeviceDiscoveryTests.swift */; };
76BF0AF82C90ACF2003BE568 /* ActionFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0AF72C90ACF2003BE568 /* ActionFactoryTests.swift */; };
76C1396A2C849A3F006CD80C /* MenuIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76C139692C849A3F006CD80C /* MenuIcons.swift */; };
76E4451229D4391000039025 /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E4451129D4391000039025 /* Onboarding.swift */; };
76E4451429D4403F00039025 /* NSNotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E4451329D4403F00039025 /* NSNotificationName.swift */; };
Expand Down Expand Up @@ -200,6 +202,8 @@
76BF0AEF2C9061E8003BE568 /* DeviceDiscoveryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceDiscoveryService.swift; sourceTree = "<group>"; };
76BF0AF12C907032003BE568 /* DeviceServiceFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceServiceFactory.swift; sourceTree = "<group>"; };
76BF0AF32C90A74E003BE568 /* AppleUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleUtilsTests.swift; sourceTree = "<group>"; };
76BF0AF52C90AB03003BE568 /* DeviceDiscoveryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceDiscoveryTests.swift; sourceTree = "<group>"; };
76BF0AF72C90ACF2003BE568 /* ActionFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionFactoryTests.swift; sourceTree = "<group>"; };
76C139692C849A3F006CD80C /* MenuIcons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuIcons.swift; sourceTree = "<group>"; };
76E4451129D4391000039025 /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
76E4451329D4403F00039025 /* NSNotificationName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNotificationName.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -451,6 +455,8 @@
76BF0ADC2C8DF660003BE568 /* AccessibilityElementTests.swift */,
76BF0AE22C8E041C003BE568 /* CustomCommandServiceTests.swift */,
76BF0AF32C90A74E003BE568 /* AppleUtilsTests.swift */,
76BF0AF52C90AB03003BE568 /* DeviceDiscoveryTests.swift */,
76BF0AF72C90ACF2003BE568 /* ActionFactoryTests.swift */,
);
path = MiniSimTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -718,10 +724,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
76BF0AF62C90AB03003BE568 /* DeviceDiscoveryTests.swift in Sources */,
7699511F2C845CBA00462287 /* DeviceParserTests.swift in Sources */,
76BF0ADD2C8DF660003BE568 /* AccessibilityElementTests.swift in Sources */,
76BF0AE32C8E041C003BE568 /* CustomCommandServiceTests.swift in Sources */,
76BF0AF42C90A74E003BE568 /* AppleUtilsTests.swift in Sources */,
76BF0AF82C90ACF2003BE568 /* ActionFactoryTests.swift in Sources */,
76B70F7E2B0D361A009D87A4 /* UserDefaultsTests.swift in Sources */,
760DEACE2B0DFB6600253576 /* ShellStub.swift in Sources */,
76B70F822B0D50FE009D87A4 /* ADBTests.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion MiniSim/MenuItems/SubMenuItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protocol SubMenuActionItem: SubMenuItem {
}

enum SubMenuItems {
enum Tags: Int {
enum Tags: Int, CaseIterable {
case copyName = 100
case copyID
case coldBoot
Expand Down
134 changes: 134 additions & 0 deletions MiniSimTests/ActionFactoryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
@testable import MiniSim
import XCTest

class MockAction: Action {
var executeWasCalled = false
var showQuestionDialogWasCalled = false
var shouldShowDialog = false

func execute() throws {
executeWasCalled = true
}

func showQuestionDialog() -> Bool {
showQuestionDialogWasCalled = true
return shouldShowDialog
}
}

class ActionFactoryTests: XCTestCase {
func testAndroidActionFactory() {
let device = Device(name: "Test Android Device", identifier: "test_id", platform: .android, type: .physical)

for tag in SubMenuItems.Tags.allCases {
let action = AndroidActionFactory.createAction(for: tag, device: device, itemName: "Test Item")
XCTAssertNotNil(action, "Action should be created for tag: \(tag)")

switch tag {
case .copyName:
XCTAssertTrue(action is CopyNameAction)
case .copyID:
XCTAssertTrue(action is CopyIDAction)
case .coldBoot:
XCTAssertTrue(action is ColdBootCommand)
case .noAudio:
XCTAssertTrue(action is NoAudioCommand)
case .toggleA11y:
XCTAssertTrue(action is ToggleA11yCommand)
case .paste:
XCTAssertTrue(action is PasteClipboardAction)
case .delete:
XCTAssertTrue(action is DeleteAction)
case .customCommand:
XCTAssertTrue(action is CustomCommandAction)
case .logcat:
XCTAssertTrue(action is LaunchLogCat)
}
}
}

func testIOSActionFactory() {
let device = Device(name: "Test iOS Device", identifier: "test_id", platform: .ios, type: .physical)

for tag in SubMenuItems.Tags.allCases {
if tag == .noAudio || tag == .toggleA11y || tag == .paste || tag == .logcat {
// These actions are not supported for iOS, so we skip them
continue
}

let action = IOSActionFactory.createAction(for: tag, device: device, itemName: "Test Item")
XCTAssertNotNil(action, "Action should be created for tag: \(tag)")

switch tag {
case .copyName:
XCTAssertTrue(action is CopyNameAction)
case .copyID:
XCTAssertTrue(action is CopyIDAction)
case .coldBoot:
XCTAssertTrue(action is ColdBootCommand)
case .delete:
XCTAssertTrue(action is DeleteAction)
case .customCommand:
XCTAssertTrue(action is CustomCommandAction)
default:
XCTFail("Unexpected tag handled: \(tag)")
}
}
}
}

class ActionExecutorTests: XCTestCase {
var executor: ActionExecutor!
var shellStub: ShellStub!

override func setUp() {
super.setUp()
executor = ActionExecutor(queue: DispatchQueue.main)
shellStub = ShellStub()
// Assume we have a way to inject the shellStub into actions that need it
}

func testExecuteAndroidAction() {
let device = Device(name: "Test Android Device", identifier: "test_id", platform: .android, type: .physical)
let expectation = self.expectation(description: "Action executed")

executor.execute(device: device, commandTag: .copyName, itemName: "Test Item")

// Use dispatch after to allow the async execution to complete
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// Here we would typically check if the action was executed
// For this test, we're just fulfilling the expectation
expectation.fulfill()
}

waitForExpectations(timeout: 1, handler: nil)
}

func testExecuteIOSAction() {
let device = Device(name: "Test iOS Device", identifier: "test_id", platform: .ios, type: .physical)
let expectation = self.expectation(description: "Action executed")

executor.execute(device: device, commandTag: .copyID, itemName: "Test Item")

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
expectation.fulfill()
}

waitForExpectations(timeout: 1, handler: nil)
}

func testExecuteActionWithQuestionDialog() {
let mockAction = MockAction()
mockAction.shouldShowDialog = true

// We need a way to inject our mock action into the factory
// This might require modifying your ActionFactory to allow injection for testing
// For now, we'll just test the logic directly

if mockAction.showQuestionDialog() {
XCTAssertFalse(mockAction.executeWasCalled, "Action should not be executed if dialog is shown")
} else {
XCTFail("Question dialog should have been shown")
}
}
}
80 changes: 80 additions & 0 deletions MiniSimTests/DeviceDiscoveryTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
@testable import MiniSim
import XCTest

class DeviceDiscoveryTests: XCTestCase {
var androidDiscovery: AndroidDeviceDiscovery!
var iosDiscovery: IOSDeviceDiscovery!
var shellStub: ShellStub!

override func setUp() {
super.setUp()
shellStub = ShellStub()
androidDiscovery = AndroidDeviceDiscovery()
androidDiscovery.shell = shellStub
iosDiscovery = IOSDeviceDiscovery()
iosDiscovery.shell = shellStub
}

override func tearDown() {
shellStub.tearDown()
super.tearDown()
}

// Android Tests
func testAndroidDeviceDiscoveryCommands() throws {
shellStub.mockedExecute = { command, arguments, _ in
if command.hasSuffix("adb") {
XCTAssertEqual(arguments, ["devices", "-l"])
return "mock adb output"
} else if command.hasSuffix("emulator") {

Check warning on line 29 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)

Check warning on line 29 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)

Check warning on line 29 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)
XCTAssertEqual(arguments, ["-list-avds"])
return "mock emulator output"
}
XCTFail("Unexpected command: \(command)")
return ""
}

_ = try androidDiscovery.getDevices(type: .physical)
_ = try androidDiscovery.getDevices(type: .virtual)
_ = try androidDiscovery.getDevices()

XCTAssertTrue(shellStub.lastExecutedCommand.contains("adb"))
}

func testAndroidCheckSetup() throws {
shellStub.mockedExecute = { _, _, _ in
"/path/to/android/sdk"
}

XCTAssertNoThrow(try androidDiscovery.checkSetup())
}

// iOS Tests
func testIOSDeviceDiscoveryCommands() throws {
shellStub.mockedExecute = { command, arguments, _ in
XCTAssertEqual(command, DeviceConstants.ProcessPaths.xcrun.rawValue)
if arguments.contains("devicectl") {
XCTAssertTrue(arguments.contains("list"))
XCTAssertTrue(arguments.contains("devices"))
return ""
} else if arguments.contains("simctl") {

Check warning on line 60 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)

Check warning on line 60 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)

Check warning on line 60 in MiniSimTests/DeviceDiscoveryTests.swift

View workflow job for this annotation

GitHub Actions / lint

Superfluous Else Violation: Else branches should be avoided when the previous if-block exits the current scope (superfluous_else)
XCTAssertEqual(arguments, ["simctl", "list", "devices", "available"])
return "mock simctl output"
}
XCTFail("Unexpected arguments: \(arguments)")
return ""
}

_ = try iosDiscovery.getDevices(type: .physical)
_ = try iosDiscovery.getDevices(type: .virtual)
_ = try iosDiscovery.getDevices()

XCTAssertTrue(shellStub.lastExecutedCommand.contains("xcrun"))
}

func testIOSCheckSetup() throws {
let xcrunPath = DeviceConstants.ProcessPaths.xcrun.rawValue
XCTAssertNoThrow(try iosDiscovery.checkSetup())
XCTAssertTrue(FileManager.default.fileExists(atPath: xcrunPath))
}
}

0 comments on commit d9790ea

Please sign in to comment.