run_loop_tcc 2.1.3
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 +7 -0
- data/LICENSE +21 -0
- data/bin/run-loop +19 -0
- data/lib/run_loop/abstract.rb +18 -0
- data/lib/run_loop/app.rb +372 -0
- data/lib/run_loop/cache/cache.rb +68 -0
- data/lib/run_loop/cli/cli.rb +48 -0
- data/lib/run_loop/cli/codesign.rb +24 -0
- data/lib/run_loop/cli/errors.rb +11 -0
- data/lib/run_loop/cli/instruments.rb +160 -0
- data/lib/run_loop/cli/locale.rb +31 -0
- data/lib/run_loop/cli/simctl.rb +257 -0
- data/lib/run_loop/cli/tcc.rb +139 -0
- data/lib/run_loop/codesign.rb +76 -0
- data/lib/run_loop/core.rb +902 -0
- data/lib/run_loop/core_simulator.rb +960 -0
- data/lib/run_loop/detect_aut/detect.rb +185 -0
- data/lib/run_loop/detect_aut/errors.rb +126 -0
- data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
- data/lib/run_loop/detect_aut/xcode.rb +157 -0
- data/lib/run_loop/device.rb +722 -0
- data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/bin/xctestctl +0 -0
- data/lib/run_loop/device_agent/cbxrunner.rb +156 -0
- data/lib/run_loop/device_agent/frameworks/Frameworks.zip +0 -0
- data/lib/run_loop/device_agent/frameworks.rb +65 -0
- data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/launcher.rb +51 -0
- data/lib/run_loop/device_agent/xcodebuild.rb +91 -0
- data/lib/run_loop/device_agent/xctestctl.rb +109 -0
- data/lib/run_loop/directory.rb +179 -0
- data/lib/run_loop/dnssd.rb +148 -0
- data/lib/run_loop/dot_dir.rb +87 -0
- data/lib/run_loop/dylib_injector.rb +145 -0
- data/lib/run_loop/encoding.rb +56 -0
- data/lib/run_loop/environment.rb +361 -0
- data/lib/run_loop/fifo.rb +40 -0
- data/lib/run_loop/host_cache.rb +128 -0
- data/lib/run_loop/http/error.rb +15 -0
- data/lib/run_loop/http/request.rb +44 -0
- data/lib/run_loop/http/retriable_client.rb +166 -0
- data/lib/run_loop/http/server.rb +17 -0
- data/lib/run_loop/instruments.rb +436 -0
- data/lib/run_loop/ipa.rb +142 -0
- data/lib/run_loop/l10n.rb +93 -0
- data/lib/run_loop/language.rb +63 -0
- data/lib/run_loop/lipo.rb +132 -0
- data/lib/run_loop/lldb.rb +52 -0
- data/lib/run_loop/locale.rb +101 -0
- data/lib/run_loop/logging.rb +111 -0
- data/lib/run_loop/otool.rb +76 -0
- data/lib/run_loop/patches/awesome_print.rb +17 -0
- data/lib/run_loop/physical_device/life_cycle.rb +268 -0
- data/lib/run_loop/plist_buddy.rb +189 -0
- data/lib/run_loop/process_terminator.rb +128 -0
- data/lib/run_loop/process_waiter.rb +117 -0
- data/lib/run_loop/regex.rb +19 -0
- data/lib/run_loop/shell.rb +103 -0
- data/lib/run_loop/sim_control.rb +1264 -0
- data/lib/run_loop/simctl.rb +275 -0
- data/lib/run_loop/sqlite.rb +61 -0
- data/lib/run_loop/strings.rb +88 -0
- data/lib/run_loop/tcc/TCC.db +0 -0
- data/lib/run_loop/tcc/tcc.rb +240 -0
- data/lib/run_loop/template.rb +61 -0
- data/lib/run_loop/version.rb +182 -0
- data/lib/run_loop/xcode.rb +318 -0
- data/lib/run_loop/xcrun.rb +107 -0
- data/lib/run_loop/xcuitest.rb +550 -0
- data/lib/run_loop.rb +230 -0
- data/plists/simctl/com.apple.UIAutomation.plist +0 -0
- data/plists/simctl/com.apple.UIAutomationPlugIn.plist +0 -0
- data/scripts/calabash_script_uia.js +28184 -0
- data/scripts/lib/json2.min.js +26 -0
- data/scripts/lib/log.js +26 -0
- data/scripts/lib/on_alert.js +224 -0
- data/scripts/read-cmd.sh +2 -0
- data/scripts/run_dismiss_location.js +89 -0
- data/scripts/run_loop_basic.js +34 -0
- data/scripts/run_loop_fast_uia.js +188 -0
- data/scripts/run_loop_host.js +117 -0
- data/scripts/run_loop_shared_element.js +125 -0
- data/scripts/timeout3 +23 -0
- data/scripts/udidetect +0 -0
- data/vendor-licenses/FBSimulatorControl.LICENSE +30 -0
- data/vendor-licenses/xctestctl.LICENSE +32 -0
- metadata +443 -0
@@ -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
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# A module for managing the ~/.run-loop directory.
|
2
|
+
module RunLoop::DotDir
|
3
|
+
|
4
|
+
def self.directory
|
5
|
+
home = RunLoop::Environment.user_home_directory
|
6
|
+
dir = File.join(home, ".run-loop")
|
7
|
+
if !File.exist?(dir)
|
8
|
+
FileUtils.mkdir_p(dir)
|
9
|
+
end
|
10
|
+
dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.make_results_dir
|
14
|
+
if RunLoop::Environment.xtc?
|
15
|
+
next_results_dir = Dir.mktmpdir("run_loop")
|
16
|
+
else
|
17
|
+
results_dir = File.join(self.directory, 'results')
|
18
|
+
next_results_dir = self.next_timestamped_dirname(results_dir)
|
19
|
+
FileUtils.mkdir_p(next_results_dir)
|
20
|
+
|
21
|
+
current = File.join(self.directory, "results", "current")
|
22
|
+
FileUtils.rm_rf(current)
|
23
|
+
FileUtils.ln_s(next_results_dir, current)
|
24
|
+
end
|
25
|
+
|
26
|
+
next_results_dir
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.rotate_result_directories
|
30
|
+
return :xtc if RunLoop::Environment.xtc?
|
31
|
+
|
32
|
+
start = Time.now
|
33
|
+
|
34
|
+
glob = "#{self.directory}/results/*"
|
35
|
+
|
36
|
+
RunLoop.log_debug("Searching for run-loop results with glob: #{glob}")
|
37
|
+
|
38
|
+
directories = Dir.glob(glob).select do |path|
|
39
|
+
File.directory?(path) && !File.symlink?(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
oldest_first = directories.sort_by { |f| File.mtime(f) }
|
43
|
+
|
44
|
+
RunLoop.log_debug("Found #{oldest_first.count} previous run-loop results")
|
45
|
+
oldest_first.pop(5)
|
46
|
+
|
47
|
+
RunLoop.log_debug("Will delete #{oldest_first.count} previous run-loop results")
|
48
|
+
|
49
|
+
oldest_first.each do |path|
|
50
|
+
FileUtils.rm_rf(path)
|
51
|
+
end
|
52
|
+
|
53
|
+
elapsed = Time.now - start
|
54
|
+
|
55
|
+
RunLoop.log_debug("Deleted #{oldest_first.count} previous results in #{elapsed} seconds")
|
56
|
+
rescue StandardError => e
|
57
|
+
RunLoop.log_error("While rotating previous results, encounterd: #{e}")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def self.timestamped_dirname(plus_seconds = 0)
|
63
|
+
(Time.now + plus_seconds).strftime("%Y-%m-%d_%H-%M-%S")
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.next_timestamped_dirname(base_dir)
|
67
|
+
dir = File.join(base_dir, self.timestamped_dirname)
|
68
|
+
return dir if !File.exist?(dir)
|
69
|
+
|
70
|
+
# Rather than wait, just increment the second. Per-second accuracy
|
71
|
+
# is not important; uniqueness is.
|
72
|
+
counter = 0
|
73
|
+
loop do
|
74
|
+
break if !File.exist?(dir)
|
75
|
+
break if counter == 4
|
76
|
+
counter = counter + 1
|
77
|
+
dir = File.join(base_dir, self.timestamped_dirname(counter))
|
78
|
+
end
|
79
|
+
|
80
|
+
# If all else fails, just return a unique UUID
|
81
|
+
if File.exist?(dir)
|
82
|
+
dir = File.join(base_dir, SecureRandom.uuid)
|
83
|
+
end
|
84
|
+
dir
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
#
|
5
|
+
# This is experimental.
|
6
|
+
#
|
7
|
+
# Injects dylibs into running executables using lldb.
|
8
|
+
class DylibInjector
|
9
|
+
|
10
|
+
# Options for controlling how often to retry dylib injection.
|
11
|
+
#
|
12
|
+
# Try 3 times for 10 seconds each try with a sleep of 2 seconds
|
13
|
+
# between tries.
|
14
|
+
#
|
15
|
+
# You can override these values if they do not work in your environment.
|
16
|
+
#
|
17
|
+
# For cucumber users, the best place to override would be in your
|
18
|
+
# features/support/env.rb.
|
19
|
+
#
|
20
|
+
# For example:
|
21
|
+
#
|
22
|
+
# RunLoop::DylibInjector::RETRY_OPTIONS[:timeout] = 60
|
23
|
+
RETRY_OPTIONS = {
|
24
|
+
:tries => 3,
|
25
|
+
:interval => 2,
|
26
|
+
:timeout => RunLoop::Environment.ci? ? 40 : 20
|
27
|
+
}
|
28
|
+
|
29
|
+
# @!attribute [r] process_name
|
30
|
+
# The name of the process to inject the dylib into. This should be obtained
|
31
|
+
# by inspecting the Info.plist in the app bundle.
|
32
|
+
# @return [String] The process_name
|
33
|
+
attr_reader :process_name
|
34
|
+
|
35
|
+
# @!attribute [r] dylib_path
|
36
|
+
# The path to the dylib that is to be injected.
|
37
|
+
# @return [String] The dylib_path
|
38
|
+
attr_reader :dylib_path
|
39
|
+
|
40
|
+
# @!visibility private
|
41
|
+
attr_reader :xcrun
|
42
|
+
|
43
|
+
# Create a new dylib injector.
|
44
|
+
# @param [String] process_name The name of the process to inject the dylib
|
45
|
+
# into. This should be obtained by inspecting the Info.plist in the app
|
46
|
+
# bundle.
|
47
|
+
# @param [String] dylib_path The path the dylib to inject.
|
48
|
+
def initialize(process_name, dylib_path)
|
49
|
+
@process_name = process_name
|
50
|
+
@dylib_path = Shellwords.shellescape(dylib_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def xcrun
|
54
|
+
@xcrun ||= RunLoop::Xcrun.new
|
55
|
+
end
|
56
|
+
|
57
|
+
# Injects a dylib into a a currently running process.
|
58
|
+
def inject_dylib(timeout)
|
59
|
+
RunLoop.log_debug("Starting lldb injection with a timeout of #{timeout} seconds")
|
60
|
+
|
61
|
+
script_path = write_script
|
62
|
+
|
63
|
+
start = Time.now
|
64
|
+
|
65
|
+
options = {
|
66
|
+
:timeout => timeout,
|
67
|
+
:log_cmd => true
|
68
|
+
}
|
69
|
+
|
70
|
+
hash = nil
|
71
|
+
success = false
|
72
|
+
begin
|
73
|
+
hash = xcrun.run_command_in_context(["lldb", "--no-lldbinit", "--source", script_path], options)
|
74
|
+
pid = hash[:pid]
|
75
|
+
exit_status = hash[:exit_status]
|
76
|
+
success = exit_status == 0
|
77
|
+
|
78
|
+
RunLoop.log_debug("lldb '#{pid}' exited with value '#{exit_status}'.")
|
79
|
+
|
80
|
+
success = exit_status == 0
|
81
|
+
elapsed = Time.now - start
|
82
|
+
|
83
|
+
if success
|
84
|
+
RunLoop.log_debug("Took #{elapsed} seconds for lldb to inject calabash dylib.")
|
85
|
+
else
|
86
|
+
RunLoop.log_debug("Could not inject dylib after #{elapsed} seconds.")
|
87
|
+
if hash[:out]
|
88
|
+
hash[:out].split("\n").each do |line|
|
89
|
+
RunLoop.log_debug(line)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
RunLoop.log_debug("lldb returned no output to stdout or stderr")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
rescue RunLoop::Xcrun::TimeoutError
|
96
|
+
elapsed = Time.now - start
|
97
|
+
RunLoop.log_debug("lldb tried for #{elapsed} seconds to inject calabash dylib before giving up.")
|
98
|
+
end
|
99
|
+
|
100
|
+
success
|
101
|
+
end
|
102
|
+
|
103
|
+
def retriable_inject_dylib(options={})
|
104
|
+
merged_options = RETRY_OPTIONS.merge(options)
|
105
|
+
|
106
|
+
tries = merged_options[:tries]
|
107
|
+
timeout = merged_options[:timeout]
|
108
|
+
interval = merged_options[:interval]
|
109
|
+
|
110
|
+
success = false
|
111
|
+
|
112
|
+
tries.times do
|
113
|
+
|
114
|
+
success = inject_dylib(timeout)
|
115
|
+
break if success
|
116
|
+
|
117
|
+
sleep(interval)
|
118
|
+
end
|
119
|
+
|
120
|
+
if !success
|
121
|
+
raise RuntimeError, "Could not inject dylib"
|
122
|
+
end
|
123
|
+
success
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def write_script
|
129
|
+
script = File.join(DotDir.directory, "inject-dylib.lldb")
|
130
|
+
|
131
|
+
if File.exist?(script)
|
132
|
+
FileUtils.rm_rf(script)
|
133
|
+
end
|
134
|
+
|
135
|
+
File.open(script, "w") do |file|
|
136
|
+
file.write("process attach -n \"#{process_name}\"\n")
|
137
|
+
file.write("expr (void*)dlopen(\"#{dylib_path}\", 0x2)\n")
|
138
|
+
file.write("detach\n")
|
139
|
+
file.write("exit\n")
|
140
|
+
end
|
141
|
+
|
142
|
+
script
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
module RunLoop
|
3
|
+
module Encoding
|
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
|
+
|
22
|
+
# Raised when a string cannot be coerced to UTF8
|
23
|
+
class UTF8Error < RuntimeError; end
|
24
|
+
|
25
|
+
# @!visibility private
|
26
|
+
def ensure_command_output_utf8(string, command)
|
27
|
+
return '' if !string
|
28
|
+
|
29
|
+
utf8 = string.force_encoding("UTF-8").chomp
|
30
|
+
|
31
|
+
return utf8 if utf8.valid_encoding?
|
32
|
+
|
33
|
+
encoded = utf8.encode("UTF-8", "UTF-8",
|
34
|
+
invalid: :replace,
|
35
|
+
undef: :replace,
|
36
|
+
replace: "")
|
37
|
+
|
38
|
+
return encoded if encoded.valid_encoding?
|
39
|
+
|
40
|
+
raise UTF8Error, %Q{
|
41
|
+
Could not force UTF-8 encoding on this string:
|
42
|
+
|
43
|
+
#{string}
|
44
|
+
|
45
|
+
which is the output of this command:
|
46
|
+
|
47
|
+
#{command}
|
48
|
+
|
49
|
+
Please file an issue with a stacktrace and the text of this error.
|
50
|
+
|
51
|
+
https://github.com/calabash/run_loop/issues
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|