run_loop 2.1.3 → 2.1.4

Sign up to get free protection for your applications and to get access to all the features.
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