plu / simctl

Ruby interface to xcrun simctl
http://www.rubydoc.info/gems/simctl
MIT License
104 stars 19 forks source link

Unable to shutdown device in current state: Creating #16

Closed UnsafePointer closed 7 years ago

UnsafePointer commented 7 years ago

Not sure if this is a simctl problem or something with the Simulator.app or CoreSimulatorService itself. Sometimes when we try to delete simulators we run into the following error:

[Hanamura] Deleting simulator with identifier 887FF917-1981-4508-85FA-0B31DA7FA6B8
/Users/jenkins/.rvm/gems/ruby-2.3.1/gems/simctl-1.6.1/lib/simctl/executor.rb:14:in `block in execute': An error was encountered processing the command (domain=com.apple.CoreSimulator.SimError, code=163): (RuntimeError)
Unable to shutdown device in current state: Creating
    from /Users/jenkins/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/open3.rb:205:in `popen_run'
    from /Users/jenkins/.rvm/rubies/ruby-2.3.1/lib/ruby/2.3.0/open3.rb:95:in `popen3'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/gems/simctl-1.6.1/lib/simctl/executor.rb:10:in `execute'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/gems/simctl-1.6.1/lib/simctl/command/shutdown.rb:9:in `shutdown_device'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/gems/simctl-1.6.1/lib/simctl.rb:39:in `method_missing'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/gems/simctl-1.6.1/lib/simctl/device.rb:174:in `shutdown'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/simulator.rb:41:in `teardown'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/command/test_all.rb:57:in `block (2 levels) in run'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/command/test_all.rb:51:in `each'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/command/test_all.rb:51:in `block in run'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/command/test_all.rb:44:in `each'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura/command/test_all.rb:44:in `run'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/gems/claide-1.0.1/lib/claide/command.rb:334:in `run'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/lib/hanamura.rb:7:in `run'
    from /Users/jenkins/Jenkins/workspace/ios-monorepo-monocomputer-master-tests/tooling/hanamura/bin/hana:6:in `<top (required)>'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/bin/hana:23:in `load'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/bin/hana:23:in `<main>'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `eval'
    from /Users/jenkins/.rvm/gems/ruby-2.3.1/bin/ruby_executable_hooks:15:in `<main>'

This is more or less how we setup/tear down simulators:

    def self.setup(destination, enable_hw_keyboard, use_fbsimctl = false, output)
      hash = self.parse(destination)
      Logger.log("Starting simulator with values #{hash}", output)
      if hash['OS'].eql? 'latest'
        simulator_runtime = SimCtl::Runtime.latest('ios')
      else
        simulator_runtime = SimCtl.runtime(name: "iOS #{hash['OS']}")
      end
      simulator = SimCtl.reset_device "Unit Tests @ #{hash['name']} #{hash['OS']}", SimCtl.devicetype(name: hash['name']), simulator_runtime
      if enable_hw_keyboard
        simulator.settings.update_hardware_keyboard!(enable_hw_keyboard)
      end
      Logger.log("Starting simulator with identifier #{simulator.udid}", output)
      unless use_fbsimctl
        simulator.launch
        simulator.wait(90) { |device| device.state == :booted && device.ready? }
      else
        cmd = [
          "/usr/local/bin/fbsimctl #{simulator.udid} boot"
        ]
        Executor.execute!(cmd, output)
      end
      simulator
    end

    def self.teardown(destination, output)
      hash = self.parse(destination)
      Logger.log("Searching for simulator with values #{hash}", output)
      simulator = SimCtl.device(name: "Unit Tests @ #{hash['name']} #{hash['OS']}")
      if simulator.nil?
        Logger.log("Simulator not found", output)
        return
      end
      Logger.log("Deleting simulator with identifier #{simulator.udid}", output)
      simulator.shutdown
      simulator.kill
      simulator.wait { |device| device.state == :shutdown }
      simulator.delete
    end
plu commented 7 years ago

A Simulator in state creating cannot be deleted, it must be in state shutdown. This limitation comes from the simctl command line tool that is executed by the simctl gem.

I'd wonder why in your teardown method the Simulator is in state creating. In your setup method you were waiting for the state :booted before you continue. So in the teardown method it's weird that the same Simulator is suddenly in state :creating. Is it by any chance not the Simulator you expect you're working with?

simulator = SimCtl.device(name: "Unit Tests @ #{hash['name']} #{hash['OS']}")

This will fetch the first Simulator that matches this name! You can have multiple Simulator with the same name. So if there's another process on the same system creating a Simulator with this name at the same time when your teardown method is running, you might end up with a different Simulator than your setup method created.

In general I recommend isolating CI jobs with different device set paths. You could have a different one per job, for example.

UnsafePointer commented 7 years ago

Sorry for the late follow up! I blame GitHub notifications 😣 Could you elaborate on the device set paths? Not sure what you mean. Can I change the location of the iOS simulator?

plu commented 7 years ago

Try this: xcrun simctl --set /tmp/simulators ... - the path must exist, so you might want to do a mkdir /tmp/simulators before.

UnsafePointer commented 7 years ago

Is there a way to pass a device set to a plain xcodebuild test-without-building command? I tried to find how you do that in plu/pxctest but looks like it's using FBSimulatorControl (and private APIs) for that.

plu commented 7 years ago

Is there a way to pass a device set to a plain xcodebuild test-without-building command?

As far is I know that's not possible, unfortunately.

UnsafePointer commented 7 years ago

Thanks for the help! The suggestion mentioned above help us to trace down the issue, it was in fact a node having more than one simulator named the same.

I'm closing this issue as this was not a problem with simctl but with our usage.