run_loop 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/run_loop.rb +119 -4
  3. data/lib/run_loop/{host_cache.rb → cache.rb} +8 -3
  4. data/lib/run_loop/codesign.rb +3 -3
  5. data/lib/run_loop/core.rb +26 -5
  6. data/lib/run_loop/core_simulator.rb +14 -13
  7. data/lib/run_loop/detect_aut/detect.rb +1 -1
  8. data/lib/run_loop/device.rb +2 -2
  9. data/lib/run_loop/device_agent/Frameworks.zip +0 -0
  10. data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
  11. data/lib/run_loop/device_agent/bin/iOSDeviceManager +0 -0
  12. data/lib/run_loop/device_agent/cbxrunner.rb +3 -2
  13. data/lib/run_loop/device_agent/frameworks.rb +7 -13
  14. data/lib/run_loop/device_agent/{xctestctl.rb → ios_device_manager.rb} +36 -24
  15. data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
  16. data/lib/run_loop/device_agent/launcher.rb +8 -0
  17. data/lib/run_loop/device_agent/xcodebuild.rb +5 -0
  18. data/lib/run_loop/dnssd.rb +148 -0
  19. data/lib/run_loop/dot_dir.rb +1 -1
  20. data/lib/run_loop/dylib_injector.rb +1 -1
  21. data/lib/run_loop/encoding.rb +17 -0
  22. data/lib/run_loop/environment.rb +15 -5
  23. data/lib/run_loop/instruments.rb +2 -2
  24. data/lib/run_loop/language.rb +4 -0
  25. data/lib/run_loop/locale.rb +4 -1
  26. data/lib/run_loop/otool.rb +1 -1
  27. data/lib/run_loop/process_terminator.rb +1 -1
  28. data/lib/run_loop/shell.rb +2 -2
  29. data/lib/run_loop/sim_control.rb +5 -5
  30. data/lib/run_loop/simctl.rb +2 -2
  31. data/lib/run_loop/strings.rb +1 -1
  32. data/lib/run_loop/version.rb +1 -1
  33. data/lib/run_loop/xcode.rb +15 -0
  34. data/lib/run_loop/xcrun.rb +7 -3
  35. data/lib/run_loop/xcuitest.rb +155 -68
  36. metadata +41 -21
  37. data/lib/run_loop/cache/cache.rb +0 -68
  38. data/lib/run_loop/device_agent/bin/xctestctl +0 -0
  39. data/lib/run_loop/device_agent/frameworks/Frameworks.zip +0 -0
@@ -4,7 +4,7 @@ module RunLoop
4
4
  module DeviceAgent
5
5
  # @!visibility private
6
6
  class Frameworks
7
- require "run_loop/shell"
7
+
8
8
  require "singleton"
9
9
  include Singleton
10
10
 
@@ -15,24 +15,23 @@ module RunLoop
15
15
  return true
16
16
  end
17
17
 
18
- RunLoop.log_debug("Installing Frameworks to #{target}")
18
+ RunLoop.log_debug("Installing Frameworks to #{rootdir}")
19
19
 
20
20
  options = { :log_cmd => true }
21
21
 
22
22
  Dir.chdir(rootdir) do
23
23
  RunLoop.log_unix_cmd("cd #{rootdir}")
24
- shell.exec(["unzip", File.basename(zip)], options)
24
+ shell.run_shell_command(["ditto", "-xk", File.basename(zip), "."], options)
25
25
  end
26
-
27
- shell.exec(["cp", "-r", "#{frameworks}/*.framework", target], options)
28
- shell.exec(["cp", "#{frameworks}/*LICENSE", target], options)
29
- RunLoop.log_debug("Installed frameworks to #{target}")
26
+ RunLoop.log_debug("Installed frameworks to #{rootdir}")
30
27
  end
31
28
 
32
29
  private
33
30
 
34
31
  # @!visibility private
32
+ # TODO replace with include Shell
35
33
  def shell
34
+ require "run_loop/shell"
36
35
  Class.new do
37
36
  include RunLoop::Shell
38
37
  def to_s; "#<Frameworks Shell>"; end
@@ -40,11 +39,6 @@ module RunLoop
40
39
  end.new
41
40
  end
42
41
 
43
- # @!visibility private
44
- def target
45
- @target ||= File.join(RunLoop::DotDir.directory, "Frameworks")
46
- end
47
-
48
42
  # @!visibility private
49
43
  def frameworks
50
44
  @frameworks ||= File.join(rootdir, "Frameworks")
@@ -57,7 +51,7 @@ module RunLoop
57
51
 
58
52
  # @!visibility private
59
53
  def rootdir
60
- @rootdir ||= File.expand_path(File.join(File.dirname(__FILE__), "frameworks"))
54
+ @rootdir ||= File.expand_path(File.join(File.dirname(__FILE__)))
61
55
  end
62
56
  end
63
57
  end
@@ -5,10 +5,10 @@ module RunLoop
5
5
  # @!visibility private
6
6
  #
7
7
  # A wrapper around the test-control binary.
8
- class XCTestctl < RunLoop::DeviceAgent::Launcher
8
+ class IOSDeviceManager < RunLoop::DeviceAgent::Launcher
9
9
 
10
10
  # @!visibility private
11
- @@xctestctl = nil
11
+ @@ios_device_manager = nil
12
12
 
13
13
  # @!visibility private
14
14
  def self.device_agent_dir
@@ -16,32 +16,36 @@ module RunLoop
16
16
  end
17
17
 
18
18
  # @!visibility private
19
- def self.xctestctl
20
- @@xctestctl ||= lambda do
21
- from_env = RunLoop::Environment.xctestctl
19
+ def self.ios_device_manager
20
+ @@ios_device_manager ||= begin
21
+ from_env = RunLoop::Environment.ios_device_manager
22
22
  if from_env
23
23
  if File.exist?(from_env)
24
- RunLoop.log_debug("Using XCTESTCTL=#{from_env}")
24
+ RunLoop.log_debug("Using IOS_DEVICE_MANAGER=#{from_env}")
25
25
  from_env
26
26
  else
27
27
  raise RuntimeError, %Q[
28
- XCTESTCTL environment variable defined:
28
+ IOS_DEVICE_MANAGER environment variable defined:
29
29
 
30
30
  #{from_env}
31
31
 
32
32
  but binary does not exist at that path.
33
- ]
33
+ ]
34
34
  end
35
-
36
35
  else
37
- File.join(self.device_agent_dir, "bin", "xctestctl")
36
+ File.join(self.device_agent_dir, "bin", "iOSDeviceManager")
38
37
  end
39
- end.call
38
+ end
39
+ end
40
+
41
+ # @!visibility private
42
+ def name
43
+ :xctestctl
40
44
  end
41
45
 
42
46
  # @!visibility private
43
47
  def to_s
44
- "#<Testctl: #{XCTestctl.xctestctl}>"
48
+ "#<iOSDeviceManager: #{IOSDeviceManager.ios_device_manager}>"
45
49
  end
46
50
 
47
51
  # @!visibility private
@@ -56,7 +60,7 @@ but binary does not exist at that path.
56
60
 
57
61
  # @!visibility private
58
62
  def self.log_file
59
- path = File.join(Launcher.dot_dir, "xctestctl.log")
63
+ path = File.join(Launcher.dot_dir, "ios-device-manager.log")
60
64
  FileUtils.touch(path) if !File.exist?(path)
61
65
  path
62
66
  end
@@ -65,17 +69,27 @@ but binary does not exist at that path.
65
69
  def launch
66
70
  RunLoop::DeviceAgent::Frameworks.instance.install
67
71
 
72
+ # WIP: it is unclear what the behavior should be.
68
73
  if device.simulator?
69
- cbxapp = RunLoop::App.new(runner.runner)
70
-
71
- # quits the simulator
72
- sim = CoreSimulator.new(device, cbxapp)
73
- sim.install
74
+ # Simulator cannot be running for this version.
75
+ CoreSimulator.quit_simulator
76
+
77
+ # TODO: run-loop is responsible for detecting an outdated CBX-Runner
78
+ # application and installing a new one. However, iOSDeviceManager
79
+ # fails if simulator is already running.
80
+
81
+ # cbxapp = RunLoop::App.new(runner.runner)
82
+ #
83
+ # # quits the simulator
84
+ # sim = CoreSimulator.new(device, cbxapp)
85
+ # sim.install
86
+ # sim.launch_simulator
74
87
  end
75
88
 
76
- cmd = RunLoop::DeviceAgent::XCTestctl.xctestctl
89
+ cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
77
90
 
78
- args = ["-r", runner.runner,
91
+ args = ["start_test",
92
+ "-r", runner.runner,
79
93
  "-t", runner.tester,
80
94
  "-d", device.udid]
81
95
 
@@ -84,14 +98,14 @@ but binary does not exist at that path.
84
98
  args << RunLoop::Environment.codesign_identity
85
99
  end
86
100
 
87
- log_file = XCTestctl.log_file
101
+ log_file = IOSDeviceManager.log_file
88
102
  FileUtils.rm_rf(log_file)
89
103
  FileUtils.touch(log_file)
90
104
 
91
105
  options = {:out => log_file, :err => log_file}
92
106
  RunLoop.log_unix_cmd("#{cmd} #{args.join(" ")} >& #{log_file}")
93
107
 
94
- # Gotta keep the xctestctl process alive or the connection
108
+ # Gotta keep the ios_device_manager process alive or the connection
95
109
  # to testmanagerd will fail.
96
110
  pid = Process.spawn(cmd, *args, options)
97
111
  Process.detach(pid)
@@ -105,5 +119,3 @@ but binary does not exist at that path.
105
119
  end
106
120
  end
107
121
  end
108
-
109
-
@@ -29,6 +29,14 @@ XCUITest is only available for iOS >= 9.0
29
29
  end
30
30
  end
31
31
 
32
+ # @!visibility private
33
+ # The name of this launcher. Must be a symbol (keyword). This value will
34
+ # be used for the key :cbx_launcher in the RunLoop::Cache so Calabash
35
+ # iOS can attach and reattach to an XCUITest instance.
36
+ def name
37
+ abstract_method!
38
+ end
39
+
32
40
  # @!visibility private
33
41
  #
34
42
  # Does whatever it takes to launch the CBX-Runner on the device.
@@ -24,6 +24,11 @@ module RunLoop
24
24
  to_s
25
25
  end
26
26
 
27
+ # @!visibility private
28
+ def name
29
+ :xcodebuild
30
+ end
31
+
27
32
  # @!visibility private
28
33
  def launch
29
34
  workspace
@@ -0,0 +1,148 @@
1
+ module RunLoop
2
+
3
+ # @!visibility private
4
+ # This class is a work in progress.
5
+ #
6
+ # At the moment, it is only useful for debugging the bonjour
7
+ # server that is started by DeviceAgent.
8
+ class DNSSD
9
+
10
+ # @!visibility private
11
+ def self.factory(json)
12
+ array = JSON.parse(json, {:symbolize_names => true})
13
+ array.map do |dns|
14
+ RunLoop::DNSSD.new(dns[:service],
15
+ dns[:ip],
16
+ dns[:port],
17
+ dns[:txt])
18
+ end
19
+ end
20
+
21
+ # @!visibility private
22
+ attr_reader :service
23
+
24
+ # @!visibility private
25
+ attr_reader :ip
26
+
27
+ # @!visibility private
28
+ attr_reader :port
29
+
30
+ # @!visibility private
31
+ attr_reader :txt
32
+
33
+ # @!visibility private
34
+ def initialize(service, ip, port, txt)
35
+ @service = service
36
+ @ip = ip
37
+ @port = port
38
+ @txt = txt
39
+ end
40
+
41
+ # @!visibility private
42
+ def url
43
+ @url ||= "http://#{ip}:#{port}"
44
+ end
45
+
46
+ # @!visibility private
47
+ def to_s
48
+ "#<#{service}: #{url} TXT: #{txt}"
49
+ end
50
+
51
+ # @!visibility private
52
+ def inspect
53
+ to_s
54
+ end
55
+
56
+ # @!visibility private
57
+ def ==(other)
58
+ service == other.service
59
+ end
60
+
61
+ DEVICE_AGENT = "_calabus._tcp"
62
+
63
+ def self.wait_for_new_device_agent(old, options={})
64
+ new = self.wait_for_device_agents(options)
65
+ new.find do |new_dns|
66
+ !old.include?(new_dns)
67
+ end
68
+ end
69
+
70
+ def self.wait_for_device_agents(options={})
71
+ default_opts = {
72
+ :timeout => 0.1,
73
+ :retries => 150,
74
+ :interval => 0.1
75
+ }
76
+
77
+ merged_opts = default_opts.merge(options)
78
+ timeout = merged_opts[:timeout]
79
+ retries = merged_opts[:retries]
80
+ interval = merged_opts[:interval]
81
+
82
+ start_time = Time.now
83
+
84
+ retries.times do |try|
85
+ time_diff = start_time + timeout - Time.now
86
+
87
+ if time_diff <= 0
88
+ elapsed = Time.now - start_time
89
+ RunLoop.log_debug("Timed out waiting after #{elapsed} seconds for DeviceAgents")
90
+ return []
91
+ end
92
+
93
+ agents = self.device_agents(timeout)
94
+
95
+ if !agents.empty?
96
+ elapsed = Time.now - start_time
97
+ RunLoop.log_debug("Found #{agents.count} DeviceAgents after #{elapsed} seconds")
98
+ return agents
99
+ end
100
+
101
+ sleep(interval)
102
+ end
103
+ []
104
+ end
105
+
106
+ def self.device_agents(timeout)
107
+ services = []
108
+ addresses = []
109
+ self.browse(DEVICE_AGENT, timeout) do |reply|
110
+ if reply.flags.add?
111
+ services << reply
112
+ end
113
+ next if reply.flags.more_coming?
114
+
115
+ services.each do |service|
116
+ resolved = service.resolve
117
+ addr = Socket.getaddrinfo(resolved.target, nil, Socket::AF_INET)
118
+ addr.each do |address|
119
+ match = addresses.find do |dns|
120
+ dns.service == service.name
121
+ end
122
+
123
+ if !match
124
+ dns = RunLoop::DNSSD.new(service.name,
125
+ addr[0][2],
126
+ resolved.port,
127
+ resolved.text_record)
128
+ addresses << dns
129
+ end
130
+ end
131
+ end
132
+ end
133
+ addresses
134
+ end
135
+
136
+ def self.browse(type, timeout)
137
+ domain = nil
138
+ flags = 0
139
+ interface = ::DNSSD::InterfaceAny
140
+ service = ::DNSSD::Service.browse(type, domain, flags, interface)
141
+ service.each(timeout) { |r| yield r }
142
+ ensure
143
+ service.stop if service
144
+ end
145
+
146
+ end
147
+ end
148
+
@@ -54,7 +54,7 @@ module RunLoop::DotDir
54
54
 
55
55
  RunLoop.log_debug("Deleted #{oldest_first.count} previous results in #{elapsed} seconds")
56
56
  rescue StandardError => e
57
- RunLoop.log_error("While rotating previous results, encounterd: #{e}")
57
+ RunLoop.log_error("While rotating previous results, encountered: #{e}")
58
58
  end
59
59
 
60
60
  private
@@ -70,7 +70,7 @@ module RunLoop
70
70
  hash = nil
71
71
  success = false
72
72
  begin
73
- hash = xcrun.exec(["lldb", "--no-lldbinit", "--source", script_path], options)
73
+ hash = xcrun.run_command_in_context(["lldb", "--no-lldbinit", "--source", script_path], options)
74
74
  pid = hash[:pid]
75
75
  exit_status = hash[:exit_status]
76
76
  success = exit_status == 0
@@ -2,6 +2,23 @@
2
2
  module RunLoop
3
3
  module Encoding
4
4
 
5
+ # Removes diacritic markers from string.
6
+ #
7
+ # The ruby Encoding tools cannot perform this action, they can only change
8
+ # convert one encodign to another by substituting characters.
9
+ #
10
+ # In ruby 1.9.3 we would have used Iconv, but that does not exist in 2.0.
11
+ #
12
+ # The Encoding::Convert in 2.0 does not work on string with UTF-16 characters.
13
+ def transliterate(string)
14
+ require "i18n"
15
+ locales = I18n.available_locales
16
+ if !locales.include?(:en)
17
+ I18n.available_locales = locales + [:en]
18
+ end
19
+ I18n.transliterate(string)
20
+ end
21
+
5
22
  # Raised when a string cannot be coerced to UTF8
6
23
  class UTF8Error < RuntimeError; end
7
24
 
@@ -186,13 +186,13 @@ module RunLoop
186
186
  end
187
187
  end
188
188
 
189
- # Returns the value of XCTESTCTL
189
+ # Returns the value of IOS_DEVICE_MANAGER
190
190
  #
191
- # Use this to specify a non-default xctestctl binary.
191
+ # Use this to specify a non-default ios_device_manager binary.
192
192
  #
193
- # The default xctestctl binary is bundled with this gem.
194
- def self.xctestctl
195
- value = ENV["XCTESTCTL"]
193
+ # The default ios_device_manager binary is bundled with this gem.
194
+ def self.ios_device_manager
195
+ value = ENV["IOS_DEVICE_MANAGER"]
196
196
  if !value || value == ""
197
197
  nil
198
198
  else
@@ -228,6 +228,16 @@ module RunLoop
228
228
  end
229
229
  end
230
230
 
231
+ # Returns the value of DEVICE_ENDPOINT
232
+ def self.device_agent_url
233
+ value = ENV["DEVICE_AGENT_URL"]
234
+ if value.nil? || value == ""
235
+ nil
236
+ else
237
+ value
238
+ end
239
+ end
240
+
231
241
  # Returns true if running in Jenkins CI
232
242
  #
233
243
  # Checks the value of JENKINS_HOME
@@ -193,7 +193,7 @@ module RunLoop
193
193
  def templates
194
194
  @instruments_templates ||= lambda do
195
195
  args = ['instruments', '-s', 'templates']
196
- hash = xcrun.exec(args, log_cmd: true)
196
+ hash = xcrun.run_command_in_context(args, log_cmd: true)
197
197
  hash[:out].chomp.split("\n").map do |elm|
198
198
  stripped = elm.strip.tr('"', '')
199
199
  if stripped == '' || stripped == 'Known Templates:'
@@ -269,7 +269,7 @@ module RunLoop
269
269
  def fetch_devices
270
270
  @device_hash ||= lambda do
271
271
  args = ['instruments', '-s', 'devices']
272
- xcrun.exec(args, log_cmd: true)
272
+ xcrun.run_command_in_context(args, log_cmd: true)
273
273
  end.call
274
274
  end
275
275