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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/bin/run-loop +19 -0
  4. data/lib/run_loop/abstract.rb +18 -0
  5. data/lib/run_loop/app.rb +372 -0
  6. data/lib/run_loop/cache/cache.rb +68 -0
  7. data/lib/run_loop/cli/cli.rb +48 -0
  8. data/lib/run_loop/cli/codesign.rb +24 -0
  9. data/lib/run_loop/cli/errors.rb +11 -0
  10. data/lib/run_loop/cli/instruments.rb +160 -0
  11. data/lib/run_loop/cli/locale.rb +31 -0
  12. data/lib/run_loop/cli/simctl.rb +257 -0
  13. data/lib/run_loop/cli/tcc.rb +139 -0
  14. data/lib/run_loop/codesign.rb +76 -0
  15. data/lib/run_loop/core.rb +902 -0
  16. data/lib/run_loop/core_simulator.rb +960 -0
  17. data/lib/run_loop/detect_aut/detect.rb +185 -0
  18. data/lib/run_loop/detect_aut/errors.rb +126 -0
  19. data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
  20. data/lib/run_loop/detect_aut/xcode.rb +157 -0
  21. data/lib/run_loop/device.rb +722 -0
  22. data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
  23. data/lib/run_loop/device_agent/bin/xctestctl +0 -0
  24. data/lib/run_loop/device_agent/cbxrunner.rb +156 -0
  25. data/lib/run_loop/device_agent/frameworks/Frameworks.zip +0 -0
  26. data/lib/run_loop/device_agent/frameworks.rb +65 -0
  27. data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
  28. data/lib/run_loop/device_agent/launcher.rb +51 -0
  29. data/lib/run_loop/device_agent/xcodebuild.rb +91 -0
  30. data/lib/run_loop/device_agent/xctestctl.rb +109 -0
  31. data/lib/run_loop/directory.rb +179 -0
  32. data/lib/run_loop/dnssd.rb +148 -0
  33. data/lib/run_loop/dot_dir.rb +87 -0
  34. data/lib/run_loop/dylib_injector.rb +145 -0
  35. data/lib/run_loop/encoding.rb +56 -0
  36. data/lib/run_loop/environment.rb +361 -0
  37. data/lib/run_loop/fifo.rb +40 -0
  38. data/lib/run_loop/host_cache.rb +128 -0
  39. data/lib/run_loop/http/error.rb +15 -0
  40. data/lib/run_loop/http/request.rb +44 -0
  41. data/lib/run_loop/http/retriable_client.rb +166 -0
  42. data/lib/run_loop/http/server.rb +17 -0
  43. data/lib/run_loop/instruments.rb +436 -0
  44. data/lib/run_loop/ipa.rb +142 -0
  45. data/lib/run_loop/l10n.rb +93 -0
  46. data/lib/run_loop/language.rb +63 -0
  47. data/lib/run_loop/lipo.rb +132 -0
  48. data/lib/run_loop/lldb.rb +52 -0
  49. data/lib/run_loop/locale.rb +101 -0
  50. data/lib/run_loop/logging.rb +111 -0
  51. data/lib/run_loop/otool.rb +76 -0
  52. data/lib/run_loop/patches/awesome_print.rb +17 -0
  53. data/lib/run_loop/physical_device/life_cycle.rb +268 -0
  54. data/lib/run_loop/plist_buddy.rb +189 -0
  55. data/lib/run_loop/process_terminator.rb +128 -0
  56. data/lib/run_loop/process_waiter.rb +117 -0
  57. data/lib/run_loop/regex.rb +19 -0
  58. data/lib/run_loop/shell.rb +103 -0
  59. data/lib/run_loop/sim_control.rb +1264 -0
  60. data/lib/run_loop/simctl.rb +275 -0
  61. data/lib/run_loop/sqlite.rb +61 -0
  62. data/lib/run_loop/strings.rb +88 -0
  63. data/lib/run_loop/tcc/TCC.db +0 -0
  64. data/lib/run_loop/tcc/tcc.rb +240 -0
  65. data/lib/run_loop/template.rb +61 -0
  66. data/lib/run_loop/version.rb +182 -0
  67. data/lib/run_loop/xcode.rb +318 -0
  68. data/lib/run_loop/xcrun.rb +107 -0
  69. data/lib/run_loop/xcuitest.rb +550 -0
  70. data/lib/run_loop.rb +230 -0
  71. data/plists/simctl/com.apple.UIAutomation.plist +0 -0
  72. data/plists/simctl/com.apple.UIAutomationPlugIn.plist +0 -0
  73. data/scripts/calabash_script_uia.js +28184 -0
  74. data/scripts/lib/json2.min.js +26 -0
  75. data/scripts/lib/log.js +26 -0
  76. data/scripts/lib/on_alert.js +224 -0
  77. data/scripts/read-cmd.sh +2 -0
  78. data/scripts/run_dismiss_location.js +89 -0
  79. data/scripts/run_loop_basic.js +34 -0
  80. data/scripts/run_loop_fast_uia.js +188 -0
  81. data/scripts/run_loop_host.js +117 -0
  82. data/scripts/run_loop_shared_element.js +125 -0
  83. data/scripts/timeout3 +23 -0
  84. data/scripts/udidetect +0 -0
  85. data/vendor-licenses/FBSimulatorControl.LICENSE +30 -0
  86. data/vendor-licenses/xctestctl.LICENSE +32 -0
  87. 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
+