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