lib/simctl/device.rb in simctl-1.5.8 vs lib/simctl/device.rb in simctl-1.6.0

- old
+ new

@@ -1,27 +1,35 @@ require 'cfpropertylist' require 'ostruct' +require 'simctl/device_launchctl' require 'simctl/device_path' require 'simctl/device_settings' require 'simctl/object' require 'timeout' module SimCtl class Device < Object attr_reader :availability, :name, :os, :state, :udid + # Returns true/false if the device is available + # + # @return [Bool] + def available? + availability !~ /unavailable/i + end + # Boots the device # # @return [void] - def boot! + def boot SimCtl.boot_device(self) end # Deletes the device # # @return [void] - def delete! + def delete SimCtl.delete_device(self) end # Returns the device type # @@ -31,88 +39,108 @@ end # Erases the device # # @return [void] - def erase! + def erase SimCtl.erase_device(self) end # Installs an app on a device # # @param path Absolute path to the app that should be installed # @return [void] - def install!(path) + def install(path) SimCtl.install_app(self, path) end # Uninstall an app from a device # # @param app_id App identifier of the app that should be uninstalled # @return [void] - def uninstall!(app_id) + def uninstall(app_id) SimCtl.uninstall_app(self, app_id) end # Kills the device # # @return [void] - def kill! + def kill SimCtl.kill_device(self) end # Launches the Simulator # # @return [void] - def launch!(scale=1.0, opts={}) + def launch(scale=1.0, opts={}) SimCtl.launch_device(self, scale, opts) end + # Returns the launchctl object + # + # @ return [SimCtl::DeviceLaunchctl] + def launchctl + @launchctl ||= DeviceLaunchctl.new(self) + end + # Launches an app in the given device # # @param opts [Hash] options hash - `{ wait_for_debugger: true/false }` # @param identifier [String] the app identifier # @param args [Array] optional launch arguments # @return [void] - def launch_app!(identifier, args=[], opts={}) + def launch_app(identifier, args=[], opts={}) SimCtl.launch_app(self, identifier, args, opts) end # Opens the url on the device # # @param url [String] The url to be opened on the device # @return [void] - def open_url!(url) + def open_url(url) SimCtl.open_url(self, url) end def path - @path ||= DevicePath.new(udid) + @path ||= DevicePath.new(self) end + # Returns true/false if the device is ready + # Uses [SimCtl::DeviceLaunchctl] to look for certain services being running. + # + # Unfortunately the 'booted' state does not mean the Simulator is ready for + # installing or launching applications. + # + # @return [Bool] + def ready? + # TODO: Should look for different services depending on device type (iphone/ipad, tv, watch) + running_services = launchctl.list.reject {|service| service.pid.to_i == 0 }.map {|service| service.name} + (required_services_for_ready - running_services).empty? + end + # Reloads the device information # # @return [void] - def reload! + def reload device = SimCtl.device(udid: udid) device.instance_variables.each do |ivar| instance_variable_set(ivar, device.instance_variable_get(ivar)) end end # Renames the device # # @return [void] - def rename!(name) + def rename(name) SimCtl.rename_device(self, name) @name = name end # Resets the device # # @return [void] - def reset! + def reset SimCtl.reset_device name, devicetype, runtime end # Resets the runtime # @@ -126,11 +154,11 @@ # @param file Path where the screenshot should be saved to # @param opts Optional hash that supports two keys: # * type: Can be png, tiff, bmp, gif, jpeg (default is png) # * display: Can be main or tv for iOS, tv for tvOS and main for watchOS # @return [void] - def screenshot!(file, opts={}) + def screenshot(file, opts={}) SimCtl.screenshot(self, file, opts) end # Returns the settings object # @@ -140,42 +168,96 @@ end # Shuts down the runtime # # @return [void] - def shutdown! + def shutdown SimCtl.shutdown_device(self) end + # Spawn a process on a device + # + # @param path [String] path to executable + # @param args [Array] arguments for the executable + # @return [void] + def spawn(path, args=[], opts={}) + SimCtl.spawn(self, path, args, opts) + end + # Returns the state of the device # # @return [sym] def state @state.downcase.to_sym end # Reloads the device until the given block returns true # # @return [void] - def wait!(timeout=SimCtl.default_timeout) + def wait(timeout=SimCtl.default_timeout) Timeout::timeout(timeout) do loop do break if yield SimCtl.device(udid: udid) end end - reload! + reload end def ==(other) return false if other.nil? return false unless other.kind_of? Device other.udid == udid end + def method_missing(method_name, *args, &block) + if method_name[-1] == '!' + new_method_name = method_name.to_s.chop.to_sym + if respond_to?(new_method_name) + warn "[#{Kernel.caller.first}] `#{method_name}` is deprecated. Please use `#{new_method_name}` instead." + return send(new_method_name, &block) + end + end + super + end + private def plist @plist ||= OpenStruct.new(CFPropertyList.native_types(CFPropertyList::List.new(file: path.device_plist).value)) end + def required_services_for_ready + case runtime.type + when :tvos, :watchos + if Xcode::Version.gte? '8.0' + [ + 'com.apple.mobileassetd', + 'com.apple.nsurlsessiond', + ] + else + [ + 'com.apple.mobileassetd', + 'com.apple.networkd', + ] + end + when :ios + if Xcode::Version.gte? '8.0' + [ + 'com.apple.SimulatorBridge', + 'com.apple.SpringBoard', + 'com.apple.backboardd', + 'com.apple.medialibraryd', + 'com.apple.mobile.installd', + ] + else + [ + 'com.apple.SimulatorBridge', + 'com.apple.SpringBoard', + 'com.apple.mobile.installd', + ] + end + else + [] + end + end end end