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.
- checksums.yaml +4 -4
- data/lib/run_loop.rb +119 -4
- data/lib/run_loop/{host_cache.rb → cache.rb} +8 -3
- data/lib/run_loop/codesign.rb +3 -3
- data/lib/run_loop/core.rb +26 -5
- data/lib/run_loop/core_simulator.rb +14 -13
- data/lib/run_loop/detect_aut/detect.rb +1 -1
- data/lib/run_loop/device.rb +2 -2
- data/lib/run_loop/device_agent/Frameworks.zip +0 -0
- data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/bin/iOSDeviceManager +0 -0
- data/lib/run_loop/device_agent/cbxrunner.rb +3 -2
- data/lib/run_loop/device_agent/frameworks.rb +7 -13
- data/lib/run_loop/device_agent/{xctestctl.rb → ios_device_manager.rb} +36 -24
- data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/launcher.rb +8 -0
- data/lib/run_loop/device_agent/xcodebuild.rb +5 -0
- data/lib/run_loop/dnssd.rb +148 -0
- data/lib/run_loop/dot_dir.rb +1 -1
- data/lib/run_loop/dylib_injector.rb +1 -1
- data/lib/run_loop/encoding.rb +17 -0
- data/lib/run_loop/environment.rb +15 -5
- data/lib/run_loop/instruments.rb +2 -2
- data/lib/run_loop/language.rb +4 -0
- data/lib/run_loop/locale.rb +4 -1
- data/lib/run_loop/otool.rb +1 -1
- data/lib/run_loop/process_terminator.rb +1 -1
- data/lib/run_loop/shell.rb +2 -2
- data/lib/run_loop/sim_control.rb +5 -5
- data/lib/run_loop/simctl.rb +2 -2
- data/lib/run_loop/strings.rb +1 -1
- data/lib/run_loop/version.rb +1 -1
- data/lib/run_loop/xcode.rb +15 -0
- data/lib/run_loop/xcrun.rb +7 -3
- data/lib/run_loop/xcuitest.rb +155 -68
- metadata +41 -21
- data/lib/run_loop/cache/cache.rb +0 -68
- data/lib/run_loop/device_agent/bin/xctestctl +0 -0
- 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
|
-
|
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 #{
|
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.
|
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__)
|
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
|
8
|
+
class IOSDeviceManager < RunLoop::DeviceAgent::Launcher
|
9
9
|
|
10
10
|
# @!visibility private
|
11
|
-
@@
|
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.
|
20
|
-
@@
|
21
|
-
from_env = RunLoop::Environment.
|
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
|
24
|
+
RunLoop.log_debug("Using IOS_DEVICE_MANAGER=#{from_env}")
|
25
25
|
from_env
|
26
26
|
else
|
27
27
|
raise RuntimeError, %Q[
|
28
|
-
|
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", "
|
36
|
+
File.join(self.device_agent_dir, "bin", "iOSDeviceManager")
|
38
37
|
end
|
39
|
-
end
|
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
|
-
"#<
|
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, "
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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::
|
89
|
+
cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
|
77
90
|
|
78
|
-
args = ["
|
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 =
|
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
|
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
|
-
|
Binary file
|
@@ -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.
|
@@ -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
|
+
|
data/lib/run_loop/dot_dir.rb
CHANGED
@@ -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,
|
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.
|
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
|
data/lib/run_loop/encoding.rb
CHANGED
@@ -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
|
|
data/lib/run_loop/environment.rb
CHANGED
@@ -186,13 +186,13 @@ module RunLoop
|
|
186
186
|
end
|
187
187
|
end
|
188
188
|
|
189
|
-
# Returns the value of
|
189
|
+
# Returns the value of IOS_DEVICE_MANAGER
|
190
190
|
#
|
191
|
-
# Use this to specify a non-default
|
191
|
+
# Use this to specify a non-default ios_device_manager binary.
|
192
192
|
#
|
193
|
-
# The default
|
194
|
-
def self.
|
195
|
-
value = ENV["
|
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
|
data/lib/run_loop/instruments.rb
CHANGED
@@ -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.
|
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.
|
272
|
+
xcrun.run_command_in_context(args, log_cmd: true)
|
273
273
|
end.call
|
274
274
|
end
|
275
275
|
|