run_loop 2.2.4 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc7f4e59c1a6af511543741a5fffc03cf6cf1ee8
4
- data.tar.gz: 7900504361dc7458895971e2ffd633e6d2caa84d
3
+ metadata.gz: 101e24777ce2bcf5ab9b3383d38bcee5b84e2380
4
+ data.tar.gz: cecaa67f6d26fcbacb586feb4cc8124fd83aeac3
5
5
  SHA512:
6
- metadata.gz: 1a4eefe49c3d3bd0a731290a278fde6926b3f9fd50df0d24b7197abecec76cf13fd8fd67c9c894852fd65dd4791dc5184f7794f4292f45c312d0519a504ed2e2
7
- data.tar.gz: 2f3b8b4f47059a1ec5ee867785e5b7f4eb09e3816b0cc3f87eb173ebf6da3665a7472b6fff3093647b7cfb012c4d63f2c9e68d08584b511a7bbd94cb7d6703c9
6
+ metadata.gz: 8f8ddac54d1977cb746bea4d974e4467d99f90fad5f6597fa5aaff86cabab2fba0cba726aee42c32cf70b78f6986b09f200ad622de295e90710fbe920b16e904
7
+ data.tar.gz: 58c40dc759901a4188ae9499892d9baaecceb8ee8a6648e2bad6e7b966bd6c498da6528bff12f7a9eed3354a5ae67cdf35176d92d92f57e7f01ce4719e4e8e02
@@ -26,6 +26,11 @@ module RunLoop
26
26
 
27
27
 
28
28
  def quit
29
+ if RunLoop::Xcode.new.version_gte_8?
30
+ puts "instruments quit with Xcode 8 is not supported"
31
+ exit 1
32
+ end
33
+
29
34
  signal = options[:signal]
30
35
  ENV['DEBUG'] = '1' if options[:debug]
31
36
  instruments = RunLoop::Instruments.new
@@ -94,6 +99,10 @@ module RunLoop
94
99
  :type => :boolean
95
100
 
96
101
  def launch
102
+ if RunLoop::Xcode.new.version_gte_8?
103
+ puts "Launching applications with Xcode 8 is not supported"
104
+ exit 1
105
+ end
97
106
 
98
107
  debug = options[:debug]
99
108
  original_value = ENV['DEBUG']
@@ -807,8 +807,8 @@ $ xcrun instruments -s templates
807
807
  # Validate the architecture.
808
808
  self.expect_simulator_compatible_arch(device, app)
809
809
 
810
- # Quits the simulator.
811
- core_sim = RunLoop::CoreSimulator.new(device, app, :xcode => xcode)
810
+ RunLoop::CoreSimulator.quit_simulator
811
+ core_sim = RunLoop::CoreSimulator.new(device, app, xcode: xcode)
812
812
 
813
813
  # Calabash 0.x can only reset the app sandbox (true/false).
814
814
  # Calabash 2.x has advanced reset options.
@@ -816,13 +816,11 @@ $ xcrun instruments -s templates
816
816
  core_sim.reset_app_sandbox
817
817
  end
818
818
 
819
- # Will quit the simulator if it is running.
820
819
  # @todo fix accessibility_enabled? so we don't have to quit the sim
821
820
  # SimControl#accessibility_enabled? is always false during Core#prepare_simulator
822
821
  # https://github.com/calabash/run_loop/issues/167
823
822
  simctl.ensure_accessibility(device)
824
823
 
825
- # Will quit the simulator if it is running.
826
824
  # @todo fix software_keyboard_enabled? so we don't have to quit the sim
827
825
  # SimControl#software_keyboard_enabled? is always false during Core#prepare_simulator
828
826
  # https://github.com/calabash/run_loop/issues/168
@@ -30,9 +30,6 @@ class RunLoop::CoreSimulator
30
30
  # This should not be overridden
31
31
  WAIT_FOR_SIMULATOR_STATE_INTERVAL = 0.1
32
32
 
33
- # @!visibility private
34
- @@simulator_pid = nil
35
-
36
33
  # @!visibility private
37
34
  attr_reader :app
38
35
 
@@ -70,12 +67,12 @@ class RunLoop::CoreSimulator
70
67
  # Not yet.
71
68
  # "com.apple.CoreSimulator.SimVerificationService",
72
69
 
73
- 'SimulatorBridge',
74
- 'configd_sim',
75
- 'CoreSimulatorBridge',
70
+ "SimulatorBridge",
71
+ "configd_sim",
72
+ "CoreSimulatorBridge",
76
73
 
77
74
  # Xcode 7
78
- 'ids_simd'
75
+ "ids_simd"
79
76
  ]
80
77
 
81
78
  # @!visibility private
@@ -84,39 +81,40 @@ class RunLoop::CoreSimulator
84
81
  SIMULATOR_QUIT_PROCESSES =
85
82
  [
86
83
  # Xcode 7 start throwing this error.
87
- ['splashboardd', false],
88
-
89
- # Xcode < 5.1
90
- ['iPhone Simulator.app', true],
91
-
92
- # 7.0 < Xcode <= 6.0
93
- ['iOS Simulator.app', true],
84
+ ["splashboardd", false],
94
85
 
95
86
  # Xcode >= 7.0
96
- ['Simulator.app', true],
87
+ ["Simulator", true],
97
88
 
98
- # Multiple launchd_sim processes have been causing problems. This
99
- # is a first pass at investigating what it would mean to kill the
100
- # launchd_sim process.
101
- ['launchd_sim', false],
89
+ # Xcode < 7.0
90
+ ["iOS Simulator", true],
91
+
92
+ # Multiple launchd_sim processes have been causing problems.
93
+ # In theory, killing the parent launchd_sim process should kill
94
+ # child processes like assetsd, but in practice this does not
95
+ # always happen.
96
+ ["launchd_sim", false],
102
97
 
103
98
  # Required for DeviceAgent termination; the simulator hangs otherwise.
104
99
  ["xpcproxy", false],
105
100
 
106
- # Causes crash reports on Xcode < 7.0
107
- ["apsd", true],
108
-
109
101
  # assetsd instances clobber each other and are not properly
110
102
  # killed when quiting the simulator.
111
- ['assetsd', false],
103
+ ["assetsd", false],
112
104
 
113
105
  # iproxy is started by UITest.
114
- ['iproxy', false],
106
+ ["iproxy", false],
115
107
 
116
108
  # Started by Xamarin Studio, this is the parent process of the
117
109
  # processes launched by Xamarin's interaction with
118
110
  # CoreSimulatorBridge.
119
- ['csproxy', false],
111
+ ["csproxy", false],
112
+
113
+ # Hundreds of these processes can be present in Xcode 8 and they
114
+ # appear to influence the behavior of DeviceAgent.
115
+ ["MobileSMSSpotlightImporter", false],
116
+ ["UserEventAgent", false],
117
+ ["mobileassetd", false]
120
118
  ]
121
119
 
122
120
  # @!visibility private
@@ -131,6 +129,29 @@ class RunLoop::CoreSimulator
131
129
  send_term_first = false
132
130
  self.term_or_kill(process_name, send_term_first)
133
131
  end
132
+
133
+ ps_name_fn = lambda do |pid|
134
+ args = ["ps", "-o", "comm=", "-p", pid.to_s]
135
+ out = RunLoop::Shell.run_shell_command(args)[:out]
136
+ if out && out.strip != ""
137
+ out.strip
138
+ else
139
+ "UNKNOWN PROCESS: #{pid}"
140
+ end
141
+ end
142
+
143
+ kill_options = { :timeout => 0.5 }
144
+
145
+ RunLoop::ProcessWaiter.pgrep_f("launchd_sim").each do |pid|
146
+ process_name = ps_name_fn.call(pid)
147
+ RunLoop::ProcessTerminator.new(pid, 'KILL', process_name, kill_options).kill_process
148
+ end
149
+
150
+ RunLoop::ProcessWaiter.pgrep_f("iPhoneSimulator").each do |pid|
151
+ process_name = ps_name_fn.call(pid)
152
+ RunLoop::ProcessTerminator.new(pid, 'KILL', process_name, kill_options).kill_process
153
+ end
154
+
134
155
  end
135
156
 
136
157
  # @!visibility private
@@ -141,8 +162,6 @@ class RunLoop::CoreSimulator
141
162
  send_term_first = process_details[1]
142
163
  self.term_or_kill(process_name, send_term_first)
143
164
  end
144
-
145
- self.simulator_pid = nil
146
165
  end
147
166
 
148
167
  # @!visibility private
@@ -288,16 +307,6 @@ class RunLoop::CoreSimulator
288
307
  installed
289
308
  end
290
309
 
291
- # @!visibility private
292
- def self.simulator_pid
293
- @@simulator_pid
294
- end
295
-
296
- # @!visibility private
297
- def self.simulator_pid=(pid)
298
- @@simulator_pid = pid
299
- end
300
-
301
310
  # @param [RunLoop::Device] device The device.
302
311
  # @param [RunLoop::App] app The application.
303
312
  # @param [Hash] options Controls the behavior of this class.
@@ -305,17 +314,9 @@ class RunLoop::CoreSimulator
305
314
  # @option options :xcode An instance of Xcode to use
306
315
  # simulators in the initialize method.
307
316
  def initialize(device, app, options={})
308
- defaults = { :quit_sim_on_init => true }
309
- merged = defaults.merge(options)
310
-
311
317
  @app = app
312
318
  @device = device
313
-
314
- @xcode = merged[:xcode]
315
-
316
- if merged[:quit_sim_on_init]
317
- RunLoop::CoreSimulator.quit_simulator
318
- end
319
+ @xcode = options[:xcode]
319
320
 
320
321
  # stdio.pipe - can cause problems finding the SHA of a simulator
321
322
  rm_instruments_pipe
@@ -341,26 +342,27 @@ class RunLoop::CoreSimulator
341
342
  @simctl ||= RunLoop::Simctl.new
342
343
  end
343
344
 
345
+ # @!visibility private
346
+ def simulator_requires_relaunch?
347
+ [simulator_state_requires_relaunch?,
348
+ running_apps_require_relaunch?].any?
349
+ end
350
+
344
351
  # Launch the simulator indicated by device.
345
352
  def launch_simulator(options={})
346
353
  merged_options = {
347
354
  :wait_for_stable => true
348
355
  }.merge(options)
349
356
 
350
- if running_simulator_pid != nil
351
- # There is a running simulator.
352
-
353
- # Did we launch it?
354
- if running_simulator_pid == RunLoop::CoreSimulator.simulator_pid
355
- # Nothing to do, we already launched the simulator.
356
- return
357
- else
358
- # We did not launch this simulator; quit it.
359
- RunLoop::CoreSimulator.quit_simulator
360
- end
357
+ if !simulator_requires_relaunch?
358
+ RunLoop.log_debug("Simulator is running and does not require a relaunch.")
359
+ return
361
360
  end
362
361
 
363
- args = ['open', '-g', '-a', sim_app_path, '--args', '-CurrentDeviceUDID', device.udid]
362
+ RunLoop::CoreSimulator.quit_simulator
363
+
364
+ args = ['open', '-g', '-a', sim_app_path, '--args',
365
+ '-CurrentDeviceUDID', device.udid, "LAUNCHED_BY_RUN_LOOP"]
364
366
 
365
367
  RunLoop.log_debug("Launching #{device} with:")
366
368
  RunLoop.log_unix_cmd("xcrun #{args.join(' ')}")
@@ -380,9 +382,6 @@ class RunLoop::CoreSimulator
380
382
  elapsed = Time.now - start_time
381
383
  RunLoop.log_debug("Took #{elapsed} seconds to launch the simulator")
382
384
 
383
- # Keep track of the pid so we can know if we have already launched this sim.
384
- RunLoop::CoreSimulator.simulator_pid = running_simulator_pid
385
-
386
385
  true
387
386
  end
388
387
 
@@ -470,15 +469,14 @@ Could not launch #{app.bundle_identifier} on #{device} after trying #{tries} tim
470
469
  def uninstall_app_and_sandbox
471
470
  return true if !app_is_installed?
472
471
 
473
- launch_simulator
474
-
475
- timeout = DEFAULT_OPTIONS[:uninstall_app_timeout]
476
- simctl.uninstall(device, app, timeout)
477
-
478
- device.simulator_wait_for_stable_state
472
+ uninstall_app_with_simctl
479
473
  true
480
474
  end
481
475
 
476
+ =begin
477
+ PRIVATE METHODS
478
+ =end
479
+
482
480
  private
483
481
 
484
482
  # @!visibility private
@@ -501,6 +499,16 @@ Could not launch #{app.bundle_identifier} on #{device} after trying #{tries} tim
501
499
  kill_options = { :timeout => 0.5 }
502
500
 
503
501
  RunLoop::ProcessWaiter.new(process_name).pids.each do |pid|
502
+
503
+ # We could try to determine if terminating the process will be successful
504
+ # by asking for the parent pid and the user id. This adds another call
505
+ # to `ps` and does not save any time. It is easier to simply let the
506
+ # ProcessTerminator fail. The downside is that a failure will appear
507
+ # in the debug log.
508
+ #
509
+ # macOS is looking more like iOS. Process names like 'mobileassetd' are
510
+ # found in both operating systems.
511
+
504
512
  killed = false
505
513
 
506
514
  if send_term_first
@@ -508,8 +516,9 @@ Could not launch #{app.bundle_identifier} on #{device} after trying #{tries} tim
508
516
  killed = term.kill_process
509
517
  end
510
518
 
511
- unless killed
512
- RunLoop::ProcessTerminator.new(pid, 'KILL', process_name, kill_options)
519
+ if !killed
520
+ term = RunLoop::ProcessTerminator.new(pid, 'KILL', process_name, kill_options)
521
+ term.kill_process
513
522
  end
514
523
  end
515
524
  end
@@ -519,13 +528,13 @@ Could not launch #{app.bundle_identifier} on #{device} after trying #{tries} tim
519
528
  # @return [String] A String suitable for searching for a pid, quitting, or
520
529
  # launching the current simulator.
521
530
  def sim_name
522
- @sim_name ||= lambda {
531
+ @sim_name ||= begin
523
532
  if xcode.version_gte_7?
524
533
  "Simulator"
525
534
  else
526
535
  "iOS Simulator"
527
536
  end
528
- }.call
537
+ end
529
538
  end
530
539
 
531
540
  # @!visibility private
@@ -534,74 +543,114 @@ Could not launch #{app.bundle_identifier} on #{device} after trying #{tries} tim
534
543
  # @return [String] The path to the simulator app for the current version of
535
544
  # Xcode.
536
545
  def sim_app_path
537
- @sim_app_path ||= lambda {
538
- dev_dir = xcode.developer_dir
539
- if xcode.version_gte_7?
540
- "#{dev_dir}/Applications/Simulator.app"
541
- else
542
- "#{dev_dir}/Applications/iOS Simulator.app"
543
- end
544
- }.call
546
+ @sim_app_path ||= begin
547
+ "#{xcode.developer_dir}/Applications/#{sim_name}.app"
548
+ end
545
549
  end
546
550
 
547
551
  # @!visibility private
548
- # Returns the current Simulator pid.
549
- #
550
- # @note Will only search for the current Xcode simulator.
551
552
  #
552
- # @return [Integer, nil] The pid as a String or nil if no process is found.
553
- def running_simulator_pid
553
+ # @return [Hash] details about the running simulator.
554
+ def running_simulator_details
554
555
  process_name = "MacOS/#{sim_name}"
555
556
 
556
- args = ["ps", "x", "-o", "pid,command"]
557
+ args = ["ps", "x", "-o", "pid=,command="]
557
558
  hash = run_shell_command(args)
558
559
 
559
560
  exit_status = hash[:exit_status]
560
561
  if exit_status != 0
561
562
  raise RuntimeError,
562
- %Q{Could not find the pid of #{sim_name} with:
563
+ %Q{Could not find the process details of #{sim_name} with:
563
564
 
564
565
  #{args.join(" ")}
565
566
 
566
- Command exited with status #{exit_status}
567
- Message: '#{hash[:out]}'
567
+ Command exited with status: #{exit_status}
568
+
569
+ '#{hash[:out]}'
568
570
  }
569
571
  end
570
572
 
571
573
  if hash[:out].nil? || hash[:out] == ""
572
574
  raise RuntimeError,
573
- %Q{Could not find the pid of #{sim_name} with:
575
+ %Q{Could not find the process details of #{sim_name} with:
574
576
 
575
577
  #{args.join(" ")}
576
578
 
577
- Command had no output
579
+ Command had no output.
578
580
  }
579
581
  end
580
582
 
581
- lines = hash[:out].split("\n")
583
+ lines = hash[:out].split($-0)
582
584
 
583
585
  match = lines.detect do |line|
584
- line[/#{process_name}/, 0]
586
+ line[/#{process_name}/]
585
587
  end
586
588
 
587
- return nil if match.nil?
589
+ return {} if match.nil?
590
+
591
+ hash = {}
592
+
593
+ pid = match.split(" ").first.strip.to_i
594
+ hash[:pid] = pid
595
+
596
+ hash[:launched_by_run_loop] = match[/LAUNCHED_BY_RUN_LOOP/]
597
+
598
+ hash
599
+ end
600
+
601
+ # @!visibility private
602
+ def uninstall_app_with_simctl
603
+ launch_simulator
604
+
605
+ app_size = RunLoop::Directory.size(app.path, :mb)
606
+ sim_size = device.simulator_size_on_disk
607
+ target_size = sim_size - app_size + 5
608
+
609
+ timeout = DEFAULT_OPTIONS[:install_app_timeout]
610
+ simctl.uninstall(device, app, timeout)
611
+
612
+ current_size = device.simulator_size_on_disk
613
+ start = Time.now
614
+ while current_size > target_size && Time.now < start + 5
615
+ sleep(0.5)
616
+ current_size = device.simulator_size_on_disk
617
+ end
588
618
 
589
- match.split(" ").first.to_i
619
+ elapsed = Time.now - start
620
+ if current_size <= target_size
621
+ RunLoop.log_debug("Waited for #{elapsed} seconds for app to uninstall")
622
+ else
623
+ RunLoop.log_debug("Timed out after #{elapsed} seconds for app to uninstall")
624
+ RunLoop.log_debug("Expected sim size #{current_size} < #{target_size}")
625
+ end
590
626
  end
591
627
 
592
628
  # @!visibility private
593
629
  def install_app_with_simctl
594
630
  launch_simulator
595
631
 
632
+ app_size = RunLoop::Directory.size(app.path, :mb)
633
+ sim_size = device.simulator_size_on_disk
634
+ target_size = sim_size + app_size
635
+
596
636
  timeout = DEFAULT_OPTIONS[:install_app_timeout]
597
637
  simctl.install(device, app, timeout)
598
638
 
599
- # On Xcode 7, we must wait. The app might not be installed otherwise. This
600
- # is particularly true for iPads where the app bundle is installed, but
601
- # SpringBoard does not detect the app has been installed.
602
- #
603
- # On Xcode 8, more testing is needed.
604
- device.simulator_wait_for_stable_state
639
+ current_size = device.simulator_size_on_disk
640
+ start = Time.now
641
+ while current_size <= target_size && Time.now < start + 5
642
+ sleep(0.5)
643
+ current_size = device.simulator_size_on_disk
644
+ end
645
+
646
+ elapsed = Time.now - start
647
+ if current_size > target_size
648
+ RunLoop.log_debug("Waited for #{elapsed} seconds for app to install")
649
+ else
650
+ RunLoop.log_debug("Timed out after #{elapsed} seconds for app to install")
651
+ RunLoop.log_debug("Expected sim size #{current_size} >= #{target_size}")
652
+ end
653
+
605
654
  installed_app_bundle_dir
606
655
  end
607
656
 
@@ -657,6 +706,86 @@ Command had no output
657
706
  device.version >= RunLoop::Version.new('8.0')
658
707
  end
659
708
 
709
+ # @!visibility private
710
+ def simulator_state_requires_relaunch?
711
+ running_sim_details = running_simulator_details
712
+
713
+ # Simulator is not running.
714
+ if !running_sim_details[:pid]
715
+ RunLoop.log_debug("Simulator relaunch required: simulator is not running.")
716
+ return true
717
+ end
718
+
719
+ # Simulator is running, but run-loop did not launch it.
720
+ if !running_sim_details[:launched_by_run_loop]
721
+ RunLoop.log_debug("Simulator relaunch required: simulator was not launched by run_loop")
722
+ return true
723
+ end
724
+
725
+ # No device was passed to initializer.
726
+ return true if device.nil?
727
+
728
+ # Simulator is running, run_loop launched it, but it is not Booted.
729
+ device.update_simulator_state
730
+ if device.state == "Booted"
731
+ RunLoop.log_debug("Simulator relaunch not required: simulator has state 'Booted'")
732
+ false
733
+ else
734
+ RunLoop.log_debug("Simulator relaunch required: simulator does not have state 'Booted'")
735
+ true
736
+ end
737
+ end
738
+
739
+ # @!visibility private
740
+ def running_apps_require_relaunch?
741
+ running_apps = device.simulator_running_app_details
742
+
743
+ if running_apps.empty?
744
+ RunLoop.log_debug("Simulator relaunch not required: no running apps")
745
+ return false
746
+ end
747
+
748
+ # DeviceAgent is running, but it was launched by Xcode.
749
+ if running_apps["XCTRunner"]
750
+ if running_apps["XCTRunner"][:args][/CBX_LAUNCHED_BY_XCODE/]
751
+ RunLoop.log_debug("Simulator relaunch required: XCTRunner is controlled by Xcode")
752
+ return true
753
+ end
754
+ end
755
+
756
+ # No app was passed to initializer.
757
+ return true if app.nil?
758
+
759
+ # AUT is running, but it was not launched by DeviceAgent.
760
+ app_name = app.executable_name
761
+ if running_apps[app_name]
762
+ launch_arg = RunLoop::DeviceAgent::Client::AUT_LAUNCHED_BY_RUN_LOOP_ARG
763
+ if !running_apps[app_name][:args][/#{launch_arg}/]
764
+ RunLoop.log_debug("Simulator relaunch required: AUT is running, but not launched by run-loop")
765
+ return true
766
+ end
767
+ end
768
+
769
+ # This is the UITest behavior. UITest does not inspect the simulator for
770
+ # system apps that are running - it only checks for running user apps.
771
+ #
772
+ # I don't think this condition is necessary, so we'll skip it for now, but
773
+ # capture there is a difference between UITest and run-loop.
774
+ #
775
+ # There is some other application running on the simulator.
776
+ # running_apps.delete("XCTRunner")
777
+ # running_apps.delete(app_name)
778
+ #
779
+ # if running_apps.empty?
780
+ # RunLoop.log_debug("Simulator relaunch not required: only XCTRunner and AUT are running")
781
+ # false
782
+ # else
783
+ # RunLoop.log_debug("Simulator relaunch required: other applications are running")
784
+ # true
785
+ # end
786
+ false
787
+ end
788
+
660
789
  # The data directory for the the device.
661
790
  #
662
791
  # ~/Library/Developer/CoreSimulator/Devices/<UDID>/data
@@ -850,20 +979,8 @@ Command had no output
850
979
  RunLoop.log_debug(" App to launch SHA: #{app_sha}")
851
980
  RunLoop.log_debug("Will install #{app}")
852
981
 
853
- FileUtils.rm_rf installed_app_bundle
854
- RunLoop.log_debug('Deleted the existing app')
855
-
856
- directory = File.expand_path(File.join(installed_app_bundle, '..'))
857
- bundle_name = File.basename(app.path)
858
- target = File.join(directory, bundle_name)
859
-
860
- args = ['ditto', app.path, target]
861
- xcrun.run_command_in_context(args, log_cmd: true)
862
-
863
- RunLoop.log_debug("Installed #{app} on CoreSimulator #{device.udid}")
864
-
865
- clear_device_launch_csstore
866
-
982
+ uninstall_app_with_simctl
983
+ install_app_with_simctl
867
984
  true
868
985
  end
869
986
 
@@ -966,7 +1083,7 @@ Command had no output
966
1083
 
967
1084
  # @!visibility private
968
1085
  def self.user_app_installed?(device, bundle_identifier)
969
- core_sim = self.new(device, bundle_identifier, {:quit_sim_on_init => false})
1086
+ core_sim = self.new(device, bundle_identifier)
970
1087
  sim_apps_dir = core_sim.send(:device_applications_dir)
971
1088
  Dir.glob("#{sim_apps_dir}/**/*.app").find do |path|
972
1089
  RunLoop::App.new(path).bundle_identifier == bundle_identifier