Skip to content

Commit

Permalink
implement ADB, Accessibility tests (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski authored Sep 8, 2024
1 parent 71c1f67 commit 0121c91
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ fastlane/test_output

iOSInjectionProject/

.DS_Store
.DS_Store
24 changes: 24 additions & 0 deletions MiniSim.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
76059BF52AD4361C0008D38B /* SetupPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76059BF42AD4361C0008D38B /* SetupPreferences.swift */; };
76059BF72AD449DC0008D38B /* OnboardingHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76059BF62AD449DC0008D38B /* OnboardingHeader.swift */; };
76059BF92AD558C30008D38B /* SetupItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76059BF82AD558C30008D38B /* SetupItemView.swift */; };
760DEACE2B0DFB6600253576 /* ShellStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 760DEACD2B0DFB6600253576 /* ShellStub.swift */; };
7610992D2A3F95850067885A /* MiniSim.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 7610992C2A3F95850067885A /* MiniSim.sdef */; };
7610992F2A3F95D90067885A /* NSScriptCommand+utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7610992E2A3F95D90067885A /* NSScriptCommand+utils.swift */; };
7625140B2992B46D0060A225 /* Pasteboard+utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7625140A2992B46D0060A225 /* Pasteboard+utils.swift */; };
Expand Down Expand Up @@ -75,8 +76,11 @@
76AC9AF62A0EA82C00864A8B /* CustomCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76AC9AF52A0EA82C00864A8B /* CustomCommands.swift */; };
76AC9AF92A0EB50800864A8B /* SymbolPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 76AC9AF82A0EB50800864A8B /* SymbolPicker */; };
76B70F7E2B0D361A009D87A4 /* UserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B70F7D2B0D361A009D87A4 /* UserDefaultsTests.swift */; };
76B70F822B0D50FE009D87A4 /* ADBTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B70F812B0D50FE009D87A4 /* ADBTests.swift */; };
76B70F842B0D5AB4009D87A4 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76B70F832B0D5AB4009D87A4 /* Shell.swift */; };
76BF0AD92C8CB3E6003BE568 /* AcknowList in Frameworks */ = {isa = PBXBuildFile; productRef = 76BF0AD82C8CB3E6003BE568 /* AcknowList */; };
76BF0ADB2C8CB4CD003BE568 /* Package.resolved in Resources */ = {isa = PBXBuildFile; fileRef = 76BF0ADA2C8CB4CD003BE568 /* Package.resolved */; };
76BF0ADD2C8DF660003BE568 /* AccessibilityElementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76BF0ADC2C8DF660003BE568 /* AccessibilityElementTests.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 @@ -115,6 +119,7 @@
76059BF42AD4361C0008D38B /* SetupPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPreferences.swift; sourceTree = "<group>"; };
76059BF62AD449DC0008D38B /* OnboardingHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingHeader.swift; sourceTree = "<group>"; };
76059BF82AD558C30008D38B /* SetupItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupItemView.swift; sourceTree = "<group>"; };
760DEACD2B0DFB6600253576 /* ShellStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellStub.swift; sourceTree = "<group>"; };
7610992C2A3F95850067885A /* MiniSim.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = MiniSim.sdef; sourceTree = "<group>"; };
7610992E2A3F95D90067885A /* NSScriptCommand+utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScriptCommand+utils.swift"; sourceTree = "<group>"; };
7625140A2992B46D0060A225 /* Pasteboard+utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Pasteboard+utils.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -169,7 +174,10 @@
76AC9AF52A0EA82C00864A8B /* CustomCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCommands.swift; sourceTree = "<group>"; };
76B70F742B0D359D009D87A4 /* MiniSimTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MiniSimTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
76B70F7D2B0D361A009D87A4 /* UserDefaultsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsTests.swift; sourceTree = "<group>"; };
76B70F812B0D50FE009D87A4 /* ADBTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADBTests.swift; sourceTree = "<group>"; };
76B70F832B0D5AB4009D87A4 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
76BF0ADA2C8CB4CD003BE568 /* Package.resolved */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Package.resolved; path = MiniSim.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved; sourceTree = SOURCE_ROOT; };
76BF0ADC2C8DF660003BE568 /* AccessibilityElementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityElementTests.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 @@ -230,6 +238,14 @@
path = Terminal;
sourceTree = "<group>";
};
760DEACC2B0DFB5B00253576 /* Mocks */ = {
isa = PBXGroup;
children = (
760DEACD2B0DFB6600253576 /* ShellStub.swift */,
);
path = Mocks;
sourceTree = "<group>";
};
762CF1E12981DDD400099999 /* Extensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -297,6 +313,7 @@
7645D4BD2982A1B100019227 /* DeviceService.swift */,
7699511C2C845B1900462287 /* DeviceParser.swift */,
76F04A10298A5AE000BF9CA3 /* ADB.swift */,
76B70F832B0D5AB4009D87A4 /* Shell.swift */,
);
path = Service;
sourceTree = "<group>";
Expand Down Expand Up @@ -396,8 +413,11 @@
76B70F752B0D359D009D87A4 /* MiniSimTests */ = {
isa = PBXGroup;
children = (
760DEACC2B0DFB5B00253576 /* Mocks */,
76B70F7D2B0D361A009D87A4 /* UserDefaultsTests.swift */,
7699511E2C845CBA00462287 /* DeviceParserTests.swift */,
76B70F812B0D50FE009D87A4 /* ADBTests.swift */,
76BF0ADC2C8DF660003BE568 /* AccessibilityElementTests.swift */,
);
path = MiniSimTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -619,6 +639,7 @@
7630B2752986D52900D8B57D /* NSAlert+showError.swift in Sources */,
4AFACC742AD730BE00EC369F /* SubMenuItem.swift in Sources */,
7630B25E2984339100D8B57D /* MainMenuActions.swift in Sources */,
76B70F842B0D5AB4009D87A4 /* Shell.swift in Sources */,
76AC9AF62A0EA82C00864A8B /* CustomCommands.swift in Sources */,
76489D5C29BFCA330070EF03 /* OnboardingItem.swift in Sources */,
7645D5012982E6FA00019227 /* main.swift in Sources */,
Expand Down Expand Up @@ -656,7 +677,10 @@
buildActionMask = 2147483647;
files = (
7699511F2C845CBA00462287 /* DeviceParserTests.swift in Sources */,
76BF0ADD2C8DF660003BE568 /* AccessibilityElementTests.swift in Sources */,
76B70F7E2B0D361A009D87A4 /* UserDefaultsTests.swift in Sources */,
760DEACE2B0DFB6600253576 /* ShellStub.swift in Sources */,
76B70F822B0D50FE009D87A4 /* ADBTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
5 changes: 3 additions & 2 deletions MiniSim/AccessibilityElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
//

import AppKit
import ShellOut

class AccessibilityElement {
static var shell: ShellProtocol = Shell()

private let underlyingElement: AXUIElement

required init(_ axUIElement: AXUIElement) {
Expand Down Expand Up @@ -44,7 +45,7 @@ class AccessibilityElement {
set frontmost of every process whose unix id is \(pid) to true
end tell'
"""
_ = try? shellOut(to: script)
_ = try? shell.execute(command: script)
}
}

Expand Down
55 changes: 43 additions & 12 deletions MiniSim/Service/Adb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@
//

import Foundation
import ShellOut

protocol ADBProtocol {
static var shell: ShellProtocol { get set }

static func getAdbPath() throws -> String
static func getEmulatorPath() throws -> String
static func getAdbId(for deviceName: String, adbPath: String) throws -> String
static func checkAndroidHome(path: String) throws -> Bool
static func isAccesibilityOn(deviceId: String, adbPath: String) -> Bool
static func getAdbId(for deviceName: String) throws -> String
static func checkAndroidHome(
path: String,
fileManager: FileManager
) throws -> Bool
static func isAccesibilityOn(deviceId: String) -> Bool
static func toggleAccesibility(deviceId: String)
}

final class ADB: ADBProtocol {
static var shell: ShellProtocol = Shell()

static let talkbackOn = "com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService"
static let talkbackOff = "com.android.talkback/com.google.android.marvin.talkback.TalkBackService"

Expand Down Expand Up @@ -55,13 +62,16 @@ final class ADB: ADBProtocol {
/**
Checks if passed path exists and points to `ANDROID_HOME`.
*/
@discardableResult static func checkAndroidHome(path: String) throws -> Bool {
if !FileManager.default.fileExists(atPath: path) {
@discardableResult static func checkAndroidHome(
path: String,
fileManager: FileManager = .default
) throws -> Bool {
if !fileManager.fileExists(atPath: path) {
throw AndroidHomeError.pathNotFound
}

do {
try shellOut(to: "\(path)" + Paths.emulator.rawValue, arguments: ["-list-avds"])
try shell.execute(command: "\(path)" + Paths.emulator.rawValue, arguments: ["-list-avds"])
} catch {
throw AndroidHomeError.pathNotCorrect
}
Expand All @@ -72,14 +82,20 @@ final class ADB: ADBProtocol {
try getAndroidHome() + Paths.emulator.rawValue
}

static func getAdbId(for deviceName: String, adbPath: String) throws -> String {
let onlineDevices = try shellOut(to: "\(adbPath) devices")
static func getAdbId(for deviceName: String) throws -> String {
let adbPath = try Self.getAdbPath()
let onlineDevices = try shell.execute(command: "\(adbPath) devices")
let splitted = onlineDevices.components(separatedBy: "\n")

for line in splitted {
let device = line.match("^emulator-[0-9]+")
guard let deviceId = device.first?.first else { continue }
let output = try? shellOut(to: "\(adbPath) -s \(deviceId) emu avd name").components(separatedBy: "\n")

let output = try? shell.execute(
command: "\(adbPath) -s \(deviceId) emu avd name"
)
.components(separatedBy: "\n")

if let name = output?.first {
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedDeviceName = deviceName.trimmingCharacters(in: .whitespacesAndNewlines)
Expand All @@ -91,9 +107,12 @@ final class ADB: ADBProtocol {
throw DeviceError.deviceNotFound
}

static func isAccesibilityOn(deviceId: String, adbPath: String) -> Bool {
static func isAccesibilityOn(deviceId: String) -> Bool {
guard let adbPath = try? Self.getAdbPath() else {
return false
}
let shellCommand = "\(adbPath) -s \(deviceId) shell settings get secure enabled_accessibility_services"
guard let result = try? shellOut(to: [shellCommand]) else {
guard let result = try? shell.execute(command: shellCommand) else {
return false
}

Expand All @@ -103,4 +122,16 @@ final class ADB: ADBProtocol {

return false
}

static func toggleAccesibility(deviceId: String) {
guard let adbPath = try? Self.getAdbPath() else {
return
}
let a11yIsEnabled = Self.isAccesibilityOn(deviceId: deviceId)
let value = a11yIsEnabled ? ADB.talkbackOff : ADB.talkbackOn
let shellCmd = "\(adbPath) -s \(deviceId) shell settings put secure enabled_accessibility_services \(value)"

// Ignore the error if toggling a11y fails.
_ = try? shell.execute(command: shellCmd)
}
}
3 changes: 1 addition & 2 deletions MiniSim/Service/DeviceParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,11 @@ class AndroidEmulatorParser: DeviceParser {
}

func parse(_ input: String) -> [Device] {
guard let adbPath = try? adb.getAdbPath() else { return [] }
let deviceNames = input.components(separatedBy: .newlines)
return deviceNames
.filter { !$0.isEmpty && !$0.contains("Storing crashdata") }
.compactMap { deviceName in
let adbId = try? adb.getAdbId(for: deviceName, adbPath: adbPath)
let adbId = try? adb.getAdbId(for: deviceName)
return Device(name: deviceName, identifier: adbId, booted: adbId != nil, platform: .android, type: .virtual)
}
}
Expand Down
2 changes: 1 addition & 1 deletion MiniSim/Service/DeviceService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ extension DeviceService {
throw DeviceError.deviceNotFound
}

let a11yIsEnabled = ADB.isAccesibilityOn(deviceId: adbId, adbPath: adbPath)
let a11yIsEnabled = ADB.isAccesibilityOn(deviceId: adbId)
let value = a11yIsEnabled ? ADB.talkbackOff : ADB.talkbackOn
let shellCmd = "\(adbPath) -s \(adbId) shell settings put secure enabled_accessibility_services \(value)"
_ = try? shellOut(to: shellCmd)
Expand Down
34 changes: 34 additions & 0 deletions MiniSim/Service/Shell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Foundation
import ShellOut

protocol ShellProtocol {
@discardableResult func execute(
command: String,
arguments: [String],
atPath: String
) throws -> String
}

extension ShellProtocol {
@discardableResult func execute(
command: String,
arguments: [String] = [],
atPath: String = "."
) throws -> String {
try execute(command: command, arguments: arguments, atPath: atPath)
}
}

final class Shell: ShellProtocol {
@discardableResult func execute(
command: String,
arguments: [String] = [],
atPath: String = "."
) throws -> String {
try shellOut(
to: command,
arguments: arguments,
at: atPath
)
}
}
12 changes: 6 additions & 6 deletions MiniSim/Views/About.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@
// Created by Oskar Kwaśniewski on 28/01/2023.
//

import AcknowList
import Sparkle
import SwiftUI
import AcknowList

struct About: View {
private let updaterController: SPUStandardUpdaterController
@Environment (\.openURL) private var openURL
@State private var isAcknowledgementsListPresented = false

init() {
updaterController = SPUStandardUpdaterController(
startingUpdater: true,
updaterDelegate: nil,
userDriverDelegate: nil
)
}

let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
private let bottomPadding: Double = 10
private let minFrameWidth: Double = 650
private let minFrameHeight: Double = 450

var body: some View {
VStack {
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
Expand All @@ -42,11 +42,11 @@ struct About: View {
Label("Check for updates", systemImage: "gear")
}
.padding(.bottom, bottomPadding)

Button("Acknowledgements") {
isAcknowledgementsListPresented.toggle()
}

HStack {
Button("GitHub") {
openURL(URL(string: "https://github.com/okwasniewski/MiniSim")!)
Expand Down
1 change: 0 additions & 1 deletion MiniSim/Views/Onboarding/SetupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Created by Oskar Kwaśniewski on 15/03/2023.
//

import ShellOut
import SwiftUI

struct SetupView: View {
Expand Down
1 change: 0 additions & 1 deletion MiniSim/Views/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import KeyboardShortcuts
import LaunchAtLogin
import Settings
import ShellOut
import SwiftUI

struct Preferences: View {
Expand Down
Loading

0 comments on commit 0121c91

Please sign in to comment.