simctl 1.6.2 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +80 -0
  4. data/.travis.yml +5 -4
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile.lock +20 -3
  7. data/README.md +3 -3
  8. data/fastlane-plugin-simctl/.gitignore +11 -0
  9. data/fastlane-plugin-simctl/.rspec +3 -0
  10. data/fastlane-plugin-simctl/.rubocop.yml +263 -0
  11. data/fastlane-plugin-simctl/Gemfile +6 -0
  12. data/fastlane-plugin-simctl/README.md +46 -0
  13. data/fastlane-plugin-simctl/Rakefile +9 -0
  14. data/fastlane-plugin-simctl/fastlane-plugin-simctl.gemspec +29 -0
  15. data/fastlane-plugin-simctl/fastlane/Fastfile +9 -0
  16. data/fastlane-plugin-simctl/fastlane/Pluginfile +1 -0
  17. data/fastlane-plugin-simctl/lib/fastlane/plugin/simctl.rb +16 -0
  18. data/fastlane-plugin-simctl/lib/fastlane/plugin/simctl/actions/simctl_action.rb +51 -0
  19. data/fastlane-plugin-simctl/lib/fastlane/plugin/simctl/helper/simctl_helper.rb +52 -0
  20. data/fastlane-plugin-simctl/lib/fastlane/plugin/simctl/version.rb +5 -0
  21. data/fastlane-plugin-simctl/spec/simctl_action_spec.rb +4 -0
  22. data/fastlane-plugin-simctl/spec/spec_helper.rb +15 -0
  23. data/lib/simctl.rb +1 -1
  24. data/lib/simctl/command/create.rb +3 -2
  25. data/lib/simctl/command/delete.rb +1 -1
  26. data/lib/simctl/command/erase.rb +0 -2
  27. data/lib/simctl/command/io.rb +3 -3
  28. data/lib/simctl/command/launch.rb +6 -6
  29. data/lib/simctl/command/list.rb +8 -6
  30. data/lib/simctl/command/reset.rb +2 -2
  31. data/lib/simctl/command/spawn.rb +4 -2
  32. data/lib/simctl/command/terminate.rb +4 -4
  33. data/lib/simctl/command/warmup.rb +3 -3
  34. data/lib/simctl/device.rb +14 -14
  35. data/lib/simctl/device_launchctl.rb +3 -3
  36. data/lib/simctl/device_path.rb +18 -7
  37. data/lib/simctl/device_settings.rb +5 -5
  38. data/lib/simctl/device_type.rb +1 -1
  39. data/lib/simctl/executor.rb +3 -3
  40. data/lib/simctl/list.rb +6 -6
  41. data/lib/simctl/object.rb +1 -1
  42. data/lib/simctl/runtime.rb +2 -2
  43. data/lib/simctl/version.rb +1 -1
  44. data/lib/simctl/xcode/path.rb +10 -4
  45. data/lib/simctl/xcode/version.rb +3 -3
  46. data/simctl.gemspec +3 -1
  47. data/spec/simctl/device_interaction_spec.rb +11 -11
  48. data/spec/simctl/executor_spec.rb +1 -1
  49. data/spec/simctl/list_spec.rb +6 -2
  50. data/spec/simctl/readme_spec.rb +5 -5
  51. data/spec/simctl/warmup_spec.rb +1 -1
  52. data/spec/spec_helper.rb +9 -13
  53. metadata +33 -2
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
6
+ eval_gemfile(plugins_path) if File.exist?(plugins_path)
@@ -0,0 +1,46 @@
1
+ # fastlane-plugin-simctl
2
+
3
+ ## Getting Started
4
+
5
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-simctl`, add it to your project by running:
6
+
7
+ ```bash
8
+ fastlane add_plugin simctl
9
+ ```
10
+
11
+ ## About fastlane-plugin-simctl
12
+
13
+ Fastlane plugin to interact with xcrun simctl. Manage your iOS Simulators directly from your Fastfile.
14
+
15
+ ## Example
16
+
17
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
18
+
19
+ ## Run tests for this plugin
20
+
21
+ To run both the tests, and code style validation, run
22
+
23
+ ```
24
+ rake
25
+ ```
26
+
27
+ To automatically fix many of the styling issues, use
28
+ ```
29
+ rubocop -a
30
+ ```
31
+
32
+ ## Issues and Feedback
33
+
34
+ For any other issues and feedback about this plugin, please submit it to this repository.
35
+
36
+ ## Troubleshooting
37
+
38
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
39
+
40
+ ## Using _fastlane_ Plugins
41
+
42
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
43
+
44
+ ## About _fastlane_
45
+
46
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new
5
+
6
+ require 'rubocop/rake_task'
7
+ RuboCop::RakeTask.new(:rubocop)
8
+
9
+ task default: [:spec, :rubocop]
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'fastlane/plugin/simctl/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'fastlane-plugin-simctl'
9
+ spec.version = Fastlane::Simctl::VERSION
10
+ spec.author = 'Renzo Crisostomo'
11
+ spec.email = 'renzo.crisostomo@me.com'
12
+
13
+ spec.summary = 'Fastlane plugin to interact with xcrun simctl'
14
+ spec.license = "MIT"
15
+
16
+ spec.files = Dir["lib/**/*"] + %w(README.md)
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'simctl', '~> 1.6.2'
21
+
22
+ spec.add_development_dependency 'pry'
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rubocop'
27
+ spec.add_development_dependency 'simplecov'
28
+ spec.add_development_dependency 'fastlane', '>= 2.53.1'
29
+ end
@@ -0,0 +1,9 @@
1
+ lane :test do
2
+ simctl(block: lambda { |other_action, device|
3
+ other_action.scan(workspace: "#{Dir.pwd}/AwesomeApp.xcworkspace",
4
+ scheme: 'AwesomeApp',
5
+ device: device.name,
6
+ derived_data_path: derived_data_path)
7
+ })
8
+ trainer(output_directory: ".")
9
+ end
@@ -0,0 +1 @@
1
+ # Autogenerated by fastlane
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/simctl/version'
2
+
3
+ module Fastlane
4
+ module Simctl
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::Simctl.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,51 @@
1
+ require 'simctl'
2
+
3
+ module Fastlane
4
+ module Actions
5
+ class SimctlAction < Action
6
+ def self.run(params)
7
+ Helper::SimctlHelper.execute_with_simulator_ready(self, params[:block], params[:runtime], params[:type], params[:name])
8
+ end
9
+
10
+ def self.description
11
+ "Fastlane plugin to interact with xcrun simctl."
12
+ end
13
+
14
+ def self.authors
15
+ ["Renzo Crisostomo"]
16
+ end
17
+
18
+ def self.details
19
+ "Fastlane plugin to interact with xcrun simctl. Manage your iOS Simulators directly from your Fastfile."
20
+ end
21
+
22
+ def self.available_options
23
+ [
24
+ FastlaneCore::ConfigItem.new(key: :block,
25
+ description: "A Ruby block given to execute in the context of a Simulator ready",
26
+ optional: false,
27
+ type: Proc),
28
+ FastlaneCore::ConfigItem.new(key: :runtime,
29
+ description: "iOS Runtime used to create the simulator",
30
+ optional: true,
31
+ type: String,
32
+ default_value: 'latest'),
33
+ FastlaneCore::ConfigItem.new(key: :type,
34
+ description: "iOS device type used to create the simulator",
35
+ optional: true,
36
+ type: String,
37
+ default_value: 'iPhone 6'),
38
+ FastlaneCore::ConfigItem.new(key: :name,
39
+ description: "String used to set the name to the simulator",
40
+ optional: true,
41
+ type: String,
42
+ default_value: nil)
43
+ ]
44
+ end
45
+
46
+ def self.is_supported?(platform)
47
+ [:ios].include?(platform)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ module Fastlane
2
+ module Helper
3
+ class SimctlHelper
4
+ def self.execute_with_simulator_ready(action, block, runtime, type, name)
5
+ device = create_device(runtime, type, name)
6
+ device.launch
7
+ device.wait(90) do |d|
8
+ Fastlane::UI.message("Waiting for simulator `#{d.name}` to be ready")
9
+ d.state == :booted && d.ready?
10
+ end
11
+ begin
12
+ block.call(action.other_action, device)
13
+ rescue StandardError => error
14
+ throw error
15
+ ensure
16
+ delete_device(device)
17
+ end
18
+ end
19
+
20
+ def self.create_device(runtime, type, name)
21
+ runtime = if runtime.eql? 'latest'
22
+ SimCtl::Runtime.latest('ios')
23
+ else
24
+ SimCtl.runtime(name: runtime)
25
+ end
26
+ device_type = SimCtl.devicetype(name: type)
27
+ device_name = name
28
+ device_name ||= type.to_s.instance_eval do |obj|
29
+ obj += "-#{ENV['JOB_NAME']}" if ENV['JOB_NAME']
30
+ obj += "@#{ENV['BUILD_NUMBER']}" if ENV['BUILD_NUMBER']
31
+ obj
32
+ end
33
+ Fastlane::UI.message("Starting simulator with runtime: `#{runtime.name}`, device type: `#{device_type.name}`"\
34
+ " and device name: `#{device_name}`")
35
+ SimCtl.reset_device(device_name, device_type, runtime)
36
+ end
37
+
38
+ def self.delete_device(device)
39
+ if device.state != :shutdown
40
+ device.shutdown
41
+ device.kill
42
+ device.wait do |d|
43
+ Fastlane::UI.message("Waiting for simulator `#{d.name}` to be shutdown")
44
+ d.state == :shutdown
45
+ end
46
+ end
47
+ Fastlane::UI.message("Deleting simulator `#{device.name}`")
48
+ device.delete
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Simctl
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ describe Fastlane::Actions::SimctlAction do
2
+ describe '#run' do
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'simplecov'
4
+
5
+ # SimpleCov.minimum_coverage 95
6
+ SimpleCov.start
7
+
8
+ # This module is only used to check the environment is currently a testing env
9
+ module SpecHelper
10
+ end
11
+
12
+ require 'fastlane' # to import the Action super class
13
+ require 'fastlane/plugin/simctl' # import the actual plugin
14
+
15
+ Fastlane.load_actions # load other actions (in case your plugin calls other actions or shared values)
@@ -30,7 +30,7 @@ module SimCtl
30
30
 
31
31
  private
32
32
 
33
- def respond_to_missing?(method_name, include_private=false)
33
+ def respond_to_missing?(method_name, include_private = false)
34
34
  command.respond_to?(method_name, include_private)
35
35
  end
36
36
 
@@ -14,10 +14,11 @@ module SimCtl
14
14
  devicetype = devicetype(name: devicetype) unless devicetype.is_a?(DeviceType)
15
15
  raise "Invalid runtime: #{runtime}" unless runtime.is_a?(Runtime)
16
16
  raise "Invalid devicetype: #{devicetype}" unless devicetype.is_a?(DeviceType)
17
- device = Executor.execute(command_for('create', Shellwords.shellescape(name), devicetype.identifier, runtime.identifier)) do |identifier|
17
+ command = command_for('create', Shellwords.shellescape(name), devicetype.identifier, runtime.identifier)
18
+ device = Executor.execute(command) do |identifier|
18
19
  device(udid: identifier)
19
20
  end
20
- device.wait {|d| d.state == :shutdown && File.exists?(d.path.device_plist)}
21
+ device.wait { |d| d.state == :shutdown && File.exist?(d.path.device_plist) }
21
22
  device
22
23
  end
23
24
  end
@@ -16,7 +16,7 @@ module SimCtl
16
16
  list_devices.each do |device|
17
17
  device.kill
18
18
  device.shutdown if device.state != :shutdown
19
- device.wait {|d| d.state == :shutdown}
19
+ device.wait { |d| d.state == :shutdown }
20
20
  device.delete
21
21
  end
22
22
  end
@@ -11,5 +11,3 @@ module SimCtl
11
11
  end
12
12
  end
13
13
  end
14
-
15
-
@@ -11,11 +11,11 @@ module SimCtl
11
11
  # * type: Can be png, tiff, bmp, gif, jpeg (default is png)
12
12
  # * display: Can be main or tv for iOS, tv for tvOS and main for watchOS
13
13
  # @return [void]
14
- def screenshot(device, file, opts={})
14
+ def screenshot(device, file, opts = {})
15
15
  unless Xcode::Version.gte? '8.2'
16
- raise UnsupportedCommandError.new('Needs at least Xcode 8.2')
16
+ raise UnsupportedCommandError, 'Needs at least Xcode 8.2'
17
17
  end
18
- optional_args = opts.map {|k,v| "--#{k}=#{Shellwords.shellescape(v)}"}
18
+ optional_args = opts.map { |k, v| "--#{k}=#{Shellwords.shellescape(v)}" }
19
19
  Executor.execute(command_for('io', device.udid, 'screenshot', *optional_args, Shellwords.shellescape(file)))
20
20
  end
21
21
  end
@@ -3,13 +3,13 @@ require 'shellwords'
3
3
  module SimCtl
4
4
  class Command
5
5
  module Launch
6
- SUPPORTED_SCALE = [1.0, 0.75, 0.5, 0.25]
6
+ SUPPORTED_SCALE = [1.0, 0.75, 0.5, 0.25].freeze
7
7
 
8
8
  # Launches a Simulator instance with the given device
9
9
  #
10
10
  # @param device [SimCtl::Device] the device to launch
11
11
  # @return [void]
12
- def launch_device(device, scale=1.0, opts={})
12
+ def launch_device(device, scale = 1.0, opts = {})
13
13
  raise "unsupported scale '#{scale}' (supported: #{SUPPORTED_SCALE.join(', ')})" unless SUPPORTED_SCALE.include?(scale)
14
14
  # Launching the same device twice does not work.
15
15
  # Simulator.app would just hang. Solution: Kill first.
@@ -17,9 +17,9 @@ module SimCtl
17
17
  args = {
18
18
  '-ConnectHardwareKeyboard' => 1,
19
19
  '-CurrentDeviceUDID' => device.udid,
20
- "-SimulatorWindowLastScale-#{device.devicetype.identifier}" => scale,
20
+ "-SimulatorWindowLastScale-#{device.devicetype.identifier}" => scale
21
21
  }
22
- args.merge!({ '-DeviceSetPath' => Shellwords.shellescape(SimCtl.device_set_path) }) unless SimCtl.device_set_path.nil?
22
+ args['-DeviceSetPath'] = Shellwords.shellescape(SimCtl.device_set_path) unless SimCtl.device_set_path.nil?
23
23
  args = args.merge(opts).zip.flatten.join(' ')
24
24
  command = "open -Fgn #{Xcode::Path.home}/Applications/Simulator.app --args #{args}"
25
25
  system command
@@ -32,8 +32,8 @@ module SimCtl
32
32
  # @param identifier [String] the app identifier
33
33
  # @param args [Array] optional launch arguments
34
34
  # @return [void]
35
- def launch_app(device, identifier, args=[], opts={})
36
- launch_args = args.map {|arg| Shellwords.shellescape arg}
35
+ def launch_app(device, identifier, args = [], opts = {})
36
+ launch_args = args.map { |arg| Shellwords.shellescape arg }
37
37
  launch_opts = opts[:wait_for_debugger] ? '-w' : ''
38
38
  Executor.execute(command_for('launch', launch_opts, device.udid, identifier, launch_args))
39
39
  end
@@ -15,7 +15,8 @@ module SimCtl
15
15
  # @return [SimCtl::DeviceType] the device type matching the given filter
16
16
  # @raise [DeviceTypeNotFound] if the device type could not be found
17
17
  def devicetype(filter)
18
- list_devicetypes.where(filter).first or raise DeviceTypeNotFound.new("Could not find a device type matching #{filter.inspect}")
18
+ device_type = list_devicetypes.where(filter).first
19
+ device_type || raise(DeviceTypeNotFound, "Could not find a device type matching #{filter.inspect}")
19
20
  end
20
21
 
21
22
  # List all devices
@@ -23,7 +24,8 @@ module SimCtl
23
24
  # @return [SimCtl::List] a list of SimCtl::Device objects
24
25
  def list_devices
25
26
  Executor.execute(command_for('list', '-j', 'devices')) do |json|
26
- SimCtl::List.new(json['devices'].map {|os, devices| devices.map {|device| Device.new(device.merge(os: os))}}.flatten)
27
+ devices = json['devices'].map { |os, devs| devs.map { |device| Device.new(device.merge(os: os)) } }
28
+ SimCtl::List.new(devices.flatten)
27
29
  end
28
30
  end
29
31
 
@@ -32,7 +34,7 @@ module SimCtl
32
34
  # @return [SimCtl::List] a list of SimCtl::DeviceType objects
33
35
  def list_devicetypes
34
36
  Executor.execute(command_for('list', '-j', 'devicetypes')) do |json|
35
- SimCtl::List.new(json['devicetypes'].map {|devicetype| DeviceType.new(devicetype)})
37
+ SimCtl::List.new(json['devicetypes'].map { |devicetype| DeviceType.new(devicetype) })
36
38
  end
37
39
  end
38
40
 
@@ -41,7 +43,7 @@ module SimCtl
41
43
  # @return [SimCtl::List] a list of SimCtl::Runtime objects
42
44
  def list_runtimes
43
45
  Executor.execute(command_for('list', '-j', 'runtimes')) do |json|
44
- SimCtl::List.new(json['runtimes'].map {|runtime| Runtime.new(runtime)})
46
+ SimCtl::List.new(json['runtimes'].map { |runtime| Runtime.new(runtime) })
45
47
  end
46
48
  end
47
49
 
@@ -51,9 +53,9 @@ module SimCtl
51
53
  # @return [SimCtl::Runtime] the runtime matching the given filter
52
54
  # @raise [RuntimeNotFound] if the runtime could not be found
53
55
  def runtime(filter)
54
- list_runtimes.where(filter).first or raise RuntimeNotFound.new("Could not find a runtime matching #{filter.inspect}")
56
+ runtime = list_runtimes.where(filter).first
57
+ runtime || raise(RuntimeNotFound, "Could not find a runtime matching #{filter.inspect}")
55
58
  end
56
-
57
59
  end
58
60
  end
59
61
  end
@@ -13,14 +13,14 @@ module SimCtl
13
13
  list_devices.where(name: name, os: runtime.name).each do |device|
14
14
  device.kill
15
15
  device.shutdown if device.state != :shutdown
16
- device.wait {|d| d.state == :shutdown}
16
+ device.wait { |d| d.state == :shutdown }
17
17
  device.delete
18
18
  end
19
19
  rescue Exception => exception
20
20
  yield exception if block_given?
21
21
  end
22
22
  device = create_device name, device_type, runtime
23
- device.wait {|d| d.state == :shutdown}
23
+ device.wait { |d| d.state == :shutdown }
24
24
  device
25
25
  end
26
26
  end
@@ -9,8 +9,10 @@ module SimCtl
9
9
  # @param path [String] path to executable
10
10
  # @param args [Array] arguments for the executable
11
11
  # @return [String] standard output the spawned process generated
12
- def spawn(device, path, args=[], opts={})
13
- Executor.execute(command_for('spawn', device.udid, Shellwords.shellescape(path), *args.map{|a| Shellwords.shellwords(a)})) do |output|
12
+ def spawn(device, path, args = [], _opts = {})
13
+ escaped_path = Shellwords.shellescape(path)
14
+ command = command_for('spawn', device.udid, escaped_path, *args.map { |a| Shellwords.shellwords(a) })
15
+ Executor.execute(command) do |output|
14
16
  output
15
17
  end
16
18
  end
@@ -9,13 +9,13 @@ module SimCtl
9
9
  # @param identifier [String] the app identifier
10
10
  # @param args [Array] optional terminate arguments
11
11
  # @return [void]
12
- def terminate_app(device, identifier, args=[])
12
+ def terminate_app(device, identifier, args = [])
13
13
  unless Xcode::Version.gte? '8.2'
14
- raise UnsupportedCommandError.new('Needs at least Xcode 8.2')
14
+ raise UnsupportedCommandError, 'Needs at least Xcode 8.2'
15
15
  end
16
- terminate_args = args.map {|arg| Shellwords.shellescape arg}
16
+ terminate_args = args.map { |arg| Shellwords.shellescape arg }
17
17
  Executor.execute(command_for('terminate', terminate_args, device.udid, identifier))
18
18
  end
19
19
  end
20
20
  end
21
- end
21
+ end