run_loop 2.0.6 → 2.0.7
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 +6 -0
- data/lib/run_loop/app.rb +15 -0
- data/lib/run_loop/cli/cli.rb +4 -0
- data/lib/run_loop/cli/codesign.rb +24 -0
- data/lib/run_loop/codesign.rb +76 -0
- data/lib/run_loop/core_simulator.rb +1 -2
- data/lib/run_loop/environment.rb +10 -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 +162 -0
- data/lib/run_loop/http/server.rb +17 -0
- data/lib/run_loop/ipa.rb +15 -0
- data/lib/run_loop/version.rb +1 -1
- data/lib/run_loop/xcode.rb +34 -6
- data/lib/run_loop/xcuitest.rb +211 -0
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48c1c78396cdf8d493ce6d242d09b2a36884fe5e
|
4
|
+
data.tar.gz: 83cfc779ce7803e4deae06af1bc7a3a55a299310
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2acc78eed572c025036a5e168b592c0ea00c2ca53cfb8562118a6e603052eccde496fd262ed1c9cb0ab2a1cbefb2d35a306829c8e7725bfc51842c404f172599
|
7
|
+
data.tar.gz: 6d0a90bf5529840121434d05ec23948bd0cc50a1be2fa5bf6616d4748154b479e8e1eff07bc7cf35447610a43805649b4a32e3ee76f987a057052bfbf8f1bf44
|
data/lib/run_loop.rb
CHANGED
@@ -14,6 +14,7 @@ require 'run_loop/fifo'
|
|
14
14
|
require 'run_loop/core'
|
15
15
|
require 'run_loop/version'
|
16
16
|
require 'run_loop/plist_buddy'
|
17
|
+
require "run_loop/codesign"
|
17
18
|
require 'run_loop/app'
|
18
19
|
require 'run_loop/ipa'
|
19
20
|
require 'run_loop/sim_control'
|
@@ -30,6 +31,11 @@ require 'run_loop/simctl/plists'
|
|
30
31
|
require 'run_loop/template'
|
31
32
|
require "run_loop/locale"
|
32
33
|
require "run_loop/language"
|
34
|
+
require "run_loop/xcuitest"
|
35
|
+
require "run_loop/http/error"
|
36
|
+
require "run_loop/http/server"
|
37
|
+
require "run_loop/http/request"
|
38
|
+
require "run_loop/http/retriable_client"
|
33
39
|
|
34
40
|
module RunLoop
|
35
41
|
|
data/lib/run_loop/app.rb
CHANGED
@@ -95,6 +95,21 @@ Bundle must:
|
|
95
95
|
version
|
96
96
|
end
|
97
97
|
|
98
|
+
# @!visibility private
|
99
|
+
def codesign_info
|
100
|
+
RunLoop::Codesign.info(path)
|
101
|
+
end
|
102
|
+
|
103
|
+
# @!visibility private
|
104
|
+
def developer_signed?
|
105
|
+
RunLoop::Codesign.developer?(path)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @!visibility private
|
109
|
+
def distribution_signed?
|
110
|
+
RunLoop::Codesign.distribution?(path)
|
111
|
+
end
|
112
|
+
|
98
113
|
# @!visibility private
|
99
114
|
# Collects the paths to executables in the bundle.
|
100
115
|
def executables
|
data/lib/run_loop/cli/cli.rb
CHANGED
@@ -4,6 +4,7 @@ require 'run_loop/cli/errors'
|
|
4
4
|
require 'run_loop/cli/instruments'
|
5
5
|
require 'run_loop/cli/simctl'
|
6
6
|
require "run_loop/cli/locale"
|
7
|
+
require "run_loop/cli/codesign"
|
7
8
|
|
8
9
|
trap 'SIGINT' do
|
9
10
|
puts 'Trapped SIGINT - exiting'
|
@@ -35,6 +36,9 @@ module RunLoop
|
|
35
36
|
desc "locale", "Tools for interacting with locales"
|
36
37
|
subcommand "locale", RunLoop::CLI::Locale
|
37
38
|
|
39
|
+
desc "codesign", "Tools for interacting with codesign"
|
40
|
+
subcommand "codesign", RunLoop::CLI::Codesign
|
41
|
+
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "run_loop"
|
3
|
+
require "run_loop/cli/errors"
|
4
|
+
|
5
|
+
module RunLoop
|
6
|
+
module CLI
|
7
|
+
class Codesign < Thor
|
8
|
+
|
9
|
+
desc "info ARTIFACT", "Print codesign information about ARTIFACT (ipa, app, or library)"
|
10
|
+
|
11
|
+
def info(app_or_ipa)
|
12
|
+
extension = File.extname(app_or_ipa)
|
13
|
+
|
14
|
+
if extension == ".app"
|
15
|
+
puts RunLoop::App.new(app_or_ipa).codesign_info
|
16
|
+
elsif extension == ".ipa"
|
17
|
+
puts RunLoop::Ipa.new(app_or_ipa).codesign_info
|
18
|
+
else
|
19
|
+
puts RunLoop::Codesign.info(app_or_ipa)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module RunLoop
|
2
|
+
# @!visibility private
|
3
|
+
# A wrapper around codesign command line tool
|
4
|
+
class Codesign
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
DEV_REGEX = /Authority=iPhone Developer:/
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
APP_STORE_REGEX = /Authority=Apple iPhone OS Application Signing/
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
DISTR_REGEX = /Authority=iPhone Distribution:/
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
NOT_SIGNED_REGEX = /code object is not signed at all/
|
17
|
+
|
18
|
+
# @!visibility private
|
19
|
+
def self.info(path)
|
20
|
+
self.expect_path_exists(path)
|
21
|
+
self.exec(["--display", "--verbose=4", path])
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!visibility private
|
25
|
+
#
|
26
|
+
# True if the asset is signed.
|
27
|
+
def self.signed?(path)
|
28
|
+
info = self.info(path)
|
29
|
+
info[NOT_SIGNED_REGEX, 0] == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!visibility private
|
33
|
+
#
|
34
|
+
# True if the asset is signed with anything other than a dev cert.
|
35
|
+
def self.distribution?(path)
|
36
|
+
info = self.info(path)
|
37
|
+
|
38
|
+
info[NOT_SIGNED_REGEX, 0] == nil &&
|
39
|
+
info[DEV_REGEX, 0] == nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!visibility private
|
43
|
+
#
|
44
|
+
# True if the asset is signed with a dev cert
|
45
|
+
def self.developer?(path)
|
46
|
+
info = self.info(path)
|
47
|
+
info[DEV_REGEX, 0] != nil
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.expect_path_exists(path)
|
53
|
+
if !File.exist?(path)
|
54
|
+
raise ArgumentError,
|
55
|
+
%Q{There is no file or directory at path:
|
56
|
+
|
57
|
+
#{path}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.exec(args)
|
63
|
+
if !args.is_a?(Array)
|
64
|
+
raise ArgumentError, "Expected args: '#{args}' to be an Array"
|
65
|
+
end
|
66
|
+
|
67
|
+
xcrun = RunLoop::Xcrun.new
|
68
|
+
cmd = ["codesign"] + args
|
69
|
+
options = {:log_cmd => true}
|
70
|
+
hash = xcrun.exec(cmd, options)
|
71
|
+
|
72
|
+
hash[:out]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -500,6 +500,7 @@ $ bundle exec run-loop simctl manage-processes
|
|
500
500
|
end
|
501
501
|
end
|
502
502
|
end
|
503
|
+
|
503
504
|
# Returns the current simulator name.
|
504
505
|
#
|
505
506
|
# @return [String] A String suitable for searching for a pid, quitting, or
|
@@ -540,8 +541,6 @@ $ bundle exec run-loop simctl manage-processes
|
|
540
541
|
# @note Will only search for the current Xcode simulator.
|
541
542
|
#
|
542
543
|
# @return [Integer, nil] The pid as a String or nil if no process is found.
|
543
|
-
#
|
544
|
-
# @todo Convert this to force UTF8
|
545
544
|
def running_simulator_pid
|
546
545
|
process_name = "MacOS/#{sim_name}"
|
547
546
|
|
data/lib/run_loop/environment.rb
CHANGED
@@ -28,6 +28,16 @@ module RunLoop
|
|
28
28
|
ENV['XAMARIN_TEST_CLOUD'] == '1'
|
29
29
|
end
|
30
30
|
|
31
|
+
# Returns the value of DEVICE_TARGET
|
32
|
+
def self.device_target
|
33
|
+
ENV["DEVICE_TARGET"]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the value of DEVICE_ENDPOINT
|
37
|
+
def self.device_endpoint
|
38
|
+
ENV["DEVICE_ENDPOINT"]
|
39
|
+
end
|
40
|
+
|
31
41
|
# Returns the value of TRACE_TEMPLATE; the Instruments template to use
|
32
42
|
# during testing.
|
33
43
|
def self.trace_template
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RunLoop
|
2
|
+
module HTTP
|
3
|
+
|
4
|
+
# Raised when there is a problem communicating with the Calabash test
|
5
|
+
# server.
|
6
|
+
class Error < StandardError
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised when there is a problem creating an HTTP request.
|
11
|
+
class RequestError < StandardError
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RunLoop
|
2
|
+
module HTTP
|
3
|
+
|
4
|
+
# A representation of an HTTP request that can be passed passed to the HTTP
|
5
|
+
# client as an argument for `get` or `post`.
|
6
|
+
# @!visibility private
|
7
|
+
class Request
|
8
|
+
attr_reader :route, :params
|
9
|
+
|
10
|
+
def initialize(route, params={})
|
11
|
+
@route = route
|
12
|
+
@params = params
|
13
|
+
end
|
14
|
+
|
15
|
+
# Create a new Request from `route` and `parameters`.
|
16
|
+
#
|
17
|
+
# @param [String] route The http route for the new request.
|
18
|
+
# @param [Array, Hash] parameters An Array or Hash of parameters.
|
19
|
+
# @return [Request] A new Request for `route` with `parameters`.
|
20
|
+
# @raise [RequestError] Raises an error if the parameters cannot be
|
21
|
+
# converted to JSON
|
22
|
+
def self.request(route, parameters)
|
23
|
+
Request.new(route, Request.data(parameters))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Converts `parameters` to JSON.
|
29
|
+
#
|
30
|
+
# @param [Array, Hash] parameters An Array or Hash of parameters.
|
31
|
+
# @return [String] A JSON formatted string that represents the parameters.
|
32
|
+
# @raise [RequestError] Raises an error if the parameters cannot be
|
33
|
+
# converted to JSON
|
34
|
+
def self.data(parameters)
|
35
|
+
begin
|
36
|
+
JSON.generate(parameters)
|
37
|
+
rescue *[TypeError, JSON::GeneratorError] => e
|
38
|
+
raise RequestError, "#{e}: could not generate JSON from '#{parameters}'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module RunLoop
|
2
|
+
module HTTP
|
3
|
+
require "httpclient"
|
4
|
+
|
5
|
+
# An HTTP client that retries its connection on errors and can time out.
|
6
|
+
# @!visibility private
|
7
|
+
class RetriableClient
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
# @!visibility private
|
11
|
+
RETRY_ON =
|
12
|
+
[
|
13
|
+
# The connection, request, or response timed out
|
14
|
+
#HTTPClient::TimeoutError,
|
15
|
+
# The address is not found. Useful for polling.
|
16
|
+
SocketError,
|
17
|
+
# The proxy could not connect to the server (Android)
|
18
|
+
# or the server is not running (iOS)
|
19
|
+
HTTPClient::KeepAliveDisconnected,
|
20
|
+
# No proxy has been set up (Android)
|
21
|
+
Errno::ECONNREFUSED,
|
22
|
+
# The server sent a partial response
|
23
|
+
#Errno::ECONNRESET,
|
24
|
+
# Client sent TCP reset (RST) before server has accepted the
|
25
|
+
# connection requested by client.
|
26
|
+
Errno::ECONNABORTED,
|
27
|
+
# The foreign function call call timed out
|
28
|
+
#Errno::ETIMEDOUT
|
29
|
+
]
|
30
|
+
|
31
|
+
# @!visibility private
|
32
|
+
HEADER =
|
33
|
+
{
|
34
|
+
'Content-Type' => 'application/json;charset=utf-8'
|
35
|
+
}
|
36
|
+
|
37
|
+
# Creates a new retriable client.
|
38
|
+
#
|
39
|
+
# This initializer takes multiple options. If the option is not
|
40
|
+
# documented, it should be considered _private_. You use undocumented
|
41
|
+
# options at your own risk.
|
42
|
+
#
|
43
|
+
# @param [RunLoop::HTTP::Server] server The server to make the HTTP request
|
44
|
+
# on.
|
45
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
46
|
+
# @option options [Number] :retries (5) How often to retry.
|
47
|
+
# @option options [Number] :timeout (5) How long to wait for a response
|
48
|
+
# before timing out.
|
49
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
50
|
+
# retries.
|
51
|
+
def initialize(server, options = {})
|
52
|
+
@client = options[:client] || ::HTTPClient.new
|
53
|
+
@server = server
|
54
|
+
@retries = options.fetch(:retries, 5)
|
55
|
+
@timeout = options.fetch(:timeout, 5)
|
56
|
+
@interval = options.fetch(:interval, 0.5)
|
57
|
+
@on_error = {}
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!visibility private
|
61
|
+
def on_error(type, &block)
|
62
|
+
@on_error[type] = block
|
63
|
+
end
|
64
|
+
|
65
|
+
# @!visibility private
|
66
|
+
def change_server(new_server)
|
67
|
+
@server = new_server
|
68
|
+
end
|
69
|
+
|
70
|
+
# Make an HTTP get request.
|
71
|
+
#
|
72
|
+
# This method takes multiple options. If the option is not documented,
|
73
|
+
# it should be considered _private_. You use undocumented options at
|
74
|
+
# your own risk.
|
75
|
+
#
|
76
|
+
# @param [RunLoop::HTTP::Request] request The request.
|
77
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
78
|
+
# @option options [Number] :retries (5) How often to retry.
|
79
|
+
# @option options [Number] :timeout (5) How long to wait for a response
|
80
|
+
# before timing out.
|
81
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
82
|
+
# retries.
|
83
|
+
def get(request, options={})
|
84
|
+
request(request, :get, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Make an HTTP post request.
|
88
|
+
#
|
89
|
+
# This method takes multiple options. If the option is not documented,
|
90
|
+
# it should be considered _private_. You use undocumented options at
|
91
|
+
# your own risk.
|
92
|
+
#
|
93
|
+
# @param [RunLoop::HTTP::Request] request The request.
|
94
|
+
# @param [Hash] options Control the retry, timeout, and interval.
|
95
|
+
# @option options [Number] :retries (5) How often to retry.
|
96
|
+
# @option options [Number] :timeout (5) How long to wait for a response
|
97
|
+
# before timing out.
|
98
|
+
# @option options [Number] :interval (0.5) How long to sleep between
|
99
|
+
# retries.
|
100
|
+
def post(request, options={})
|
101
|
+
request(request, :post, options)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def request(request, request_method, options={})
|
107
|
+
retries = options.fetch(:retries, @retries)
|
108
|
+
timeout = options.fetch(:timeout, @timeout)
|
109
|
+
interval = options.fetch(:interval, @interval)
|
110
|
+
header = options.fetch(:header, HEADER)
|
111
|
+
|
112
|
+
RunLoop.log_debug("HTTP: #{@server.endpoint + request.route} #{options}")
|
113
|
+
|
114
|
+
start_time = Time.now
|
115
|
+
last_error = nil
|
116
|
+
|
117
|
+
client = @client.dup
|
118
|
+
client.receive_timeout = timeout
|
119
|
+
|
120
|
+
retries.times do |i|
|
121
|
+
first_try = i == 0
|
122
|
+
|
123
|
+
# Subtract the aggregate time we've spent thus far to make sure we're
|
124
|
+
# not exceeding the request timeout across retries.
|
125
|
+
time_diff = start_time + timeout - Time.now
|
126
|
+
|
127
|
+
if time_diff <= 0
|
128
|
+
raise HTTP::Error, 'Timeout exceeded'
|
129
|
+
end
|
130
|
+
|
131
|
+
client.receive_timeout = [time_diff, client.receive_timeout].min
|
132
|
+
|
133
|
+
begin
|
134
|
+
return client.send(request_method, @server.endpoint + request.route,
|
135
|
+
request.params, header)
|
136
|
+
rescue *RETRY_ON => e
|
137
|
+
RunLoop.log_debug("Rescued http error: #{e}")
|
138
|
+
|
139
|
+
if first_try
|
140
|
+
if @on_error[e.class]
|
141
|
+
@on_error[e.class].call(@server)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
last_error = e
|
146
|
+
sleep interval
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# We should raise helpful messages
|
151
|
+
if last_error.is_a?(HTTPClient::KeepAliveDisconnected)
|
152
|
+
raise HTTP::Error, "#{last_error}: It is likely your server has crashed."
|
153
|
+
elsif last_error.is_a?(SocketError)
|
154
|
+
raise HTTP::Error, "#{last_error}: Did your server start and is it on the same network?"
|
155
|
+
end
|
156
|
+
|
157
|
+
raise HTTP::Error, last_error
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module RunLoop
|
2
|
+
module HTTP
|
3
|
+
|
4
|
+
# A representation of the RunLoop test server.
|
5
|
+
# @!visibility private
|
6
|
+
class Server
|
7
|
+
attr_reader :endpoint
|
8
|
+
|
9
|
+
# @param [URI] endpoint The endpoint to reach the test server.
|
10
|
+
# running on the device. The port should be included in the URI.
|
11
|
+
def initialize(endpoint)
|
12
|
+
@endpoint = endpoint
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/lib/run_loop/ipa.rb
CHANGED
@@ -51,6 +51,21 @@ module RunLoop
|
|
51
51
|
app.calabash_server_version
|
52
52
|
end
|
53
53
|
|
54
|
+
# @!visibility private
|
55
|
+
def codesign_info
|
56
|
+
app.codesign_info
|
57
|
+
end
|
58
|
+
|
59
|
+
# @!visibility private
|
60
|
+
def developer_signed?
|
61
|
+
app.developer_signed?
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!visibility private
|
65
|
+
def distribution_signed?
|
66
|
+
app.distribution_signed?
|
67
|
+
end
|
68
|
+
|
54
69
|
private
|
55
70
|
|
56
71
|
# @!visibility private
|
data/lib/run_loop/version.rb
CHANGED
data/lib/run_loop/xcode.rb
CHANGED
@@ -216,13 +216,41 @@ module RunLoop
|
|
216
216
|
#```
|
217
217
|
#
|
218
218
|
# @return [String] path to current developer directory
|
219
|
+
#
|
220
|
+
# @raise [RuntimeError] If path to Xcode.app/Contents/Developer
|
221
|
+
# cannot be determined.
|
219
222
|
def developer_dir
|
220
|
-
@xcode_developer_dir ||=
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
223
|
+
@xcode_developer_dir ||= lambda do
|
224
|
+
if RunLoop::Environment.developer_dir
|
225
|
+
path = RunLoop::Environment.developer_dir
|
226
|
+
else
|
227
|
+
path = xcode_select_path
|
228
|
+
end
|
229
|
+
|
230
|
+
if !File.directory?(path)
|
231
|
+
raise RuntimeError,
|
232
|
+
%Q{Cannot determine the active Xcode. Expected an Xcode here:
|
233
|
+
|
234
|
+
#{path}
|
235
|
+
|
236
|
+
Check the value of xcode-select:
|
237
|
+
|
238
|
+
# Does this resolve to a valid Xcode.app/Contents/Developer path?
|
239
|
+
$ xcode-select --print-path
|
240
|
+
|
241
|
+
Is the DEVELOPER_DIR variable set in your environment? You would
|
242
|
+
only use this if you have multiple Xcode's installed.
|
243
|
+
|
244
|
+
$ echo $DEVELOPER_DIR
|
245
|
+
|
246
|
+
See the man pages for xcrun and xcode-select for details.
|
247
|
+
|
248
|
+
$ man xcrun
|
249
|
+
$ man xcode-select
|
250
|
+
}
|
251
|
+
end
|
252
|
+
path
|
253
|
+
end.call
|
226
254
|
end
|
227
255
|
|
228
256
|
private
|
@@ -0,0 +1,211 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
class XCUITest
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
DEFAULTS = {
|
8
|
+
:port => 27753,
|
9
|
+
:simulator_ip => "127.0.0.1"
|
10
|
+
}
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
def self.workspace
|
14
|
+
value = ENV["XCUITEST_WORKSPACE"]
|
15
|
+
if value.nil? || value == ""
|
16
|
+
return nil
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def self.log_file
|
24
|
+
path = File.join(XCUITest.dot_dir, "xcuitest.log")
|
25
|
+
|
26
|
+
if !File.exist?(path)
|
27
|
+
FileUtils.touch(path)
|
28
|
+
end
|
29
|
+
path
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!visibility private
|
33
|
+
def initialize(bundle_id)
|
34
|
+
@bundle_id = bundle_id
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
# TODO: move to Device ?
|
39
|
+
# TODO: needs tests for device case
|
40
|
+
def url
|
41
|
+
if target.simulator?
|
42
|
+
"http://#{DEFAULTS[:simulator_ip]}:#{DEFAULTS[:port]}"
|
43
|
+
else
|
44
|
+
calabash_endpoint = RunLoop::Environment.device_endpoint
|
45
|
+
if calabash_endpoint
|
46
|
+
base = calabash_endpoint.split(":")[0..1].join(":")
|
47
|
+
"http://#{base}:#{DEFAULTS[:port]}"
|
48
|
+
else
|
49
|
+
device_name = target.name.gsub(/[\'\s]/, "")
|
50
|
+
encoding_options = {
|
51
|
+
:invalid => :replace, # Replace invalid byte sequences
|
52
|
+
:undef => :replace, # Replace anything not defined in ASCII
|
53
|
+
:replace => '' # Use a blank for those replacements
|
54
|
+
}
|
55
|
+
encoded = device_name.encode(Encoding.find("ASCII"), encoding_options)
|
56
|
+
"http://#{encoded}.local:27753"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @!visibility private
|
62
|
+
def launch_calabus_driver
|
63
|
+
|
64
|
+
driver_url = url
|
65
|
+
server = RunLoop::HTTP::Server.new(driver_url)
|
66
|
+
request = RunLoop::HTTP::Request.new("/shutdown", {})
|
67
|
+
options = {
|
68
|
+
:timeout => 0.5,
|
69
|
+
:retries => 1
|
70
|
+
}
|
71
|
+
client = RunLoop::HTTP::RetriableClient.new(server, options)
|
72
|
+
|
73
|
+
begin
|
74
|
+
response = client.post(request)
|
75
|
+
RunLoop.log_debug("Calabus driver says, \"#{response.body}\"")
|
76
|
+
sleep(2.0)
|
77
|
+
rescue => e
|
78
|
+
RunLoop.log_debug("Driver shutdown raised #{e}")
|
79
|
+
end
|
80
|
+
|
81
|
+
workspace = XCUITest.workspace
|
82
|
+
|
83
|
+
if !workspace || !File.directory?(workspace)
|
84
|
+
raise RuntimeError, "No workspace found"
|
85
|
+
end
|
86
|
+
|
87
|
+
destination = target.udid
|
88
|
+
|
89
|
+
# might be nil
|
90
|
+
if target.simulator?
|
91
|
+
# quits the simulator
|
92
|
+
sim = CoreSimulator.new(target, "")
|
93
|
+
sim.launch_simulator
|
94
|
+
else
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
args = [
|
99
|
+
"xcrun",
|
100
|
+
"xcodebuild",
|
101
|
+
"-scheme", "CBXAppStub",
|
102
|
+
"-workspace", workspace,
|
103
|
+
"-config", "Debug",
|
104
|
+
"-destination", "id=#{destination}",
|
105
|
+
"clean",
|
106
|
+
"test"
|
107
|
+
]
|
108
|
+
|
109
|
+
log_file = XCUITest.log_file
|
110
|
+
|
111
|
+
options = {
|
112
|
+
:out => log_file,
|
113
|
+
:err => log_file
|
114
|
+
}
|
115
|
+
|
116
|
+
command = args.join(" ")
|
117
|
+
RunLoop.log_unix_cmd("#{command} >& #{log_file}")
|
118
|
+
|
119
|
+
pid = Process.spawn(*args, options)
|
120
|
+
Process.detach(pid)
|
121
|
+
|
122
|
+
if target.simulator?
|
123
|
+
target.simulator_wait_for_stable_state
|
124
|
+
end
|
125
|
+
|
126
|
+
RunLoop.log_debug("Waiting for CBX-Runner to build...")
|
127
|
+
|
128
|
+
server = RunLoop::HTTP::Server.new(driver_url)
|
129
|
+
request = RunLoop::HTTP::Request.new("/health", {})
|
130
|
+
|
131
|
+
options = {
|
132
|
+
:timeout => 60,
|
133
|
+
:interval => 0.1,
|
134
|
+
:retries => 600
|
135
|
+
}
|
136
|
+
|
137
|
+
client = RunLoop::HTTP::RetriableClient.new(server, options)
|
138
|
+
response = client.get(request)
|
139
|
+
|
140
|
+
RunLoop.log_debug("Calabus driver says, \"#{response.body}\"")
|
141
|
+
pid.to_i
|
142
|
+
end
|
143
|
+
|
144
|
+
def launch_app
|
145
|
+
server = RunLoop::HTTP::Server.new(url)
|
146
|
+
request = RunLoop::HTTP::Request.request("/session", {:bundleID => bundle_id})
|
147
|
+
client = RunLoop::HTTP::RetriableClient.new(server)
|
148
|
+
response = client.post(request)
|
149
|
+
|
150
|
+
RunLoop.log_debug("Calabus driver says, \"#{response.body}\"")
|
151
|
+
end
|
152
|
+
|
153
|
+
# @!visibility private
|
154
|
+
def target
|
155
|
+
@device ||= lambda do
|
156
|
+
target = RunLoop::Environment.device_target
|
157
|
+
|
158
|
+
if !target
|
159
|
+
target = RunLoop::Core.default_simulator
|
160
|
+
end
|
161
|
+
|
162
|
+
options = {
|
163
|
+
:sim_control => simctl,
|
164
|
+
:instruments => instruments
|
165
|
+
}
|
166
|
+
|
167
|
+
device = RunLoop::Device.device_with_identifier(target, options)
|
168
|
+
|
169
|
+
if !device
|
170
|
+
raise RuntimeError, "Could not find a device"
|
171
|
+
end
|
172
|
+
|
173
|
+
device
|
174
|
+
end.call
|
175
|
+
end
|
176
|
+
|
177
|
+
# @!visibility private
|
178
|
+
def bundle_id
|
179
|
+
@bundle_id
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
# @!visibility private
|
185
|
+
def simctl
|
186
|
+
@simctl ||= RunLoop::SimControl.new
|
187
|
+
end
|
188
|
+
|
189
|
+
# @!visibility private
|
190
|
+
def instruments
|
191
|
+
@instruments ||= RunLoop::Instruments.new
|
192
|
+
end
|
193
|
+
|
194
|
+
# @!visibility private
|
195
|
+
def xcrun
|
196
|
+
RunLoop::Xcrun.new
|
197
|
+
end
|
198
|
+
|
199
|
+
# @!visibility private
|
200
|
+
def self.dot_dir
|
201
|
+
path = File.join(RunLoop::DotDir.directory, "xcuitest")
|
202
|
+
|
203
|
+
if !File.exist?(path)
|
204
|
+
FileUtils.mkdir_p(path)
|
205
|
+
end
|
206
|
+
|
207
|
+
path
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: run_loop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Krukow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -86,6 +86,20 @@ dependencies:
|
|
86
86
|
- - ">="
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: 0.0.2
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: httpclient
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.6'
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '2.6'
|
89
103
|
- !ruby/object:Gem::Dependency
|
90
104
|
name: rspec_junit_formatter
|
91
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,10 +288,12 @@ files:
|
|
274
288
|
- lib/run_loop/app.rb
|
275
289
|
- lib/run_loop/cache/cache.rb
|
276
290
|
- lib/run_loop/cli/cli.rb
|
291
|
+
- lib/run_loop/cli/codesign.rb
|
277
292
|
- lib/run_loop/cli/errors.rb
|
278
293
|
- lib/run_loop/cli/instruments.rb
|
279
294
|
- lib/run_loop/cli/locale.rb
|
280
295
|
- lib/run_loop/cli/simctl.rb
|
296
|
+
- lib/run_loop/codesign.rb
|
281
297
|
- lib/run_loop/core.rb
|
282
298
|
- lib/run_loop/core_simulator.rb
|
283
299
|
- lib/run_loop/device.rb
|
@@ -287,6 +303,10 @@ files:
|
|
287
303
|
- lib/run_loop/environment.rb
|
288
304
|
- lib/run_loop/fifo.rb
|
289
305
|
- lib/run_loop/host_cache.rb
|
306
|
+
- lib/run_loop/http/error.rb
|
307
|
+
- lib/run_loop/http/request.rb
|
308
|
+
- lib/run_loop/http/retriable_client.rb
|
309
|
+
- lib/run_loop/http/server.rb
|
290
310
|
- lib/run_loop/instruments.rb
|
291
311
|
- lib/run_loop/ipa.rb
|
292
312
|
- lib/run_loop/l10n.rb
|
@@ -308,6 +328,7 @@ files:
|
|
308
328
|
- lib/run_loop/version.rb
|
309
329
|
- lib/run_loop/xcode.rb
|
310
330
|
- lib/run_loop/xcrun.rb
|
331
|
+
- lib/run_loop/xcuitest.rb
|
311
332
|
- plists/simctl/com.apple.UIAutomation.plist
|
312
333
|
- plists/simctl/com.apple.UIAutomationPlugIn.plist
|
313
334
|
- scripts/calabash_script_uia.js
|
@@ -342,10 +363,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
342
363
|
version: '0'
|
343
364
|
requirements: []
|
344
365
|
rubyforge_project:
|
345
|
-
rubygems_version: 2.5.
|
366
|
+
rubygems_version: 2.5.2
|
346
367
|
signing_key:
|
347
368
|
specification_version: 4
|
348
369
|
summary: The bridge between Calabash iOS and Xcode command-line tools like instruments
|
349
370
|
and simctl.
|
350
371
|
test_files: []
|
351
|
-
has_rdoc:
|