run_loop 2.0.7 → 2.0.8
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 +4 -0
- data/lib/run_loop/app.rb +90 -19
- data/lib/run_loop/detect_aut/detect.rb +96 -0
- data/lib/run_loop/detect_aut/errors.rb +127 -0
- data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
- data/lib/run_loop/detect_aut/xcode.rb +136 -0
- data/lib/run_loop/environment.rb +43 -0
- data/lib/run_loop/ipa.rb +5 -0
- data/lib/run_loop/otool.rb +1 -1
- data/lib/run_loop/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a796cf1b151815127a2c4ee38c6fadf13be9cef8
|
4
|
+
data.tar.gz: 40e6b8e223325fba4d2a13683afa1609b2f9348c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0b6d5bb57703550c6592a5d665c1d11d74db9075bf8b688156e2f6094efd6ab0e3efc72c19e9080228f1da8d003f8d115382f804303228a091d05043d3a7309
|
7
|
+
data.tar.gz: 8b53742a9c3b4fe65036f387e93dd189ff350e57bd24ebd90bb7c24e1f6635543a675aafb9500bdf34b92640d61ed6adb5c8eb81e9c9833fcab2f6588d28d7a7
|
data/lib/run_loop.rb
CHANGED
@@ -17,6 +17,10 @@ require 'run_loop/plist_buddy'
|
|
17
17
|
require "run_loop/codesign"
|
18
18
|
require 'run_loop/app'
|
19
19
|
require 'run_loop/ipa'
|
20
|
+
require "run_loop/detect_aut/errors"
|
21
|
+
require "run_loop/detect_aut/xamarin_studio"
|
22
|
+
require "run_loop/detect_aut/xcode"
|
23
|
+
require "run_loop/detect_aut/detect"
|
20
24
|
require 'run_loop/sim_control'
|
21
25
|
require 'run_loop/device'
|
22
26
|
require 'run_loop/instruments'
|
data/lib/run_loop/app.rb
CHANGED
@@ -25,7 +25,8 @@ Bundle must:
|
|
25
25
|
|
26
26
|
1. be a directory that exists,
|
27
27
|
2. have a .app extension,
|
28
|
-
3.
|
28
|
+
3. contain an Info.plist,
|
29
|
+
4. and the app binary (CFBundleExecutable) must exist
|
29
30
|
}
|
30
31
|
end
|
31
32
|
end
|
@@ -49,10 +50,12 @@ Bundle must:
|
|
49
50
|
def self.valid?(app_bundle_path)
|
50
51
|
return false if app_bundle_path.nil?
|
51
52
|
|
52
|
-
File.
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
return false if !File.directory?(app_bundle_path)
|
54
|
+
return false if !File.extname(app_bundle_path) == ".app"
|
55
|
+
|
56
|
+
return false if !self.info_plist_exist?(app_bundle_path)
|
57
|
+
return false if !self.executable_file_exist?(app_bundle_path)
|
58
|
+
true
|
56
59
|
end
|
57
60
|
|
58
61
|
# Returns the Info.plist path.
|
@@ -66,7 +69,7 @@ Bundle must:
|
|
66
69
|
# @raise [RuntimeError] If the plist cannot be read or the
|
67
70
|
# CFBundleIdentifier is empty or does not exist.
|
68
71
|
def bundle_identifier
|
69
|
-
identifier = plist_buddy.plist_read(
|
72
|
+
identifier = plist_buddy.plist_read("CFBundleIdentifier", info_plist_path)
|
70
73
|
unless identifier
|
71
74
|
raise "Expected key 'CFBundleIdentifier' in '#{info_plist_path}'"
|
72
75
|
end
|
@@ -78,11 +81,28 @@ Bundle must:
|
|
78
81
|
# @raise [RuntimeError] If the plist cannot be read or the
|
79
82
|
# CFBundleExecutable is empty or does not exist.
|
80
83
|
def executable_name
|
81
|
-
|
82
|
-
unless
|
84
|
+
name = plist_buddy.plist_read("CFBundleExecutable", info_plist_path)
|
85
|
+
unless name
|
83
86
|
raise "Expected key 'CFBundleExecutable' in '#{info_plist_path}'"
|
84
87
|
end
|
85
|
-
|
88
|
+
name
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the arches for the binary.
|
92
|
+
def arches
|
93
|
+
@arches ||= lipo.info
|
94
|
+
end
|
95
|
+
|
96
|
+
# True if the app has been built for the simulator
|
97
|
+
def simulator?
|
98
|
+
arches.include?("i386") || arches.include?("x86_64")
|
99
|
+
end
|
100
|
+
|
101
|
+
# True if the app has been built for physical devices
|
102
|
+
def physical_device?
|
103
|
+
arches.any? do |arch|
|
104
|
+
arch[/arm/, 0]
|
105
|
+
end
|
86
106
|
end
|
87
107
|
|
88
108
|
# Inspects the app's file for the server version
|
@@ -115,7 +135,6 @@ Bundle must:
|
|
115
135
|
def executables
|
116
136
|
executables = []
|
117
137
|
Dir.glob("#{path}/**/*") do |file|
|
118
|
-
next if File.directory?(file)
|
119
138
|
next if skip_executable_check?(file)
|
120
139
|
if otool(file).executable?
|
121
140
|
executables << file
|
@@ -131,6 +150,30 @@ Bundle must:
|
|
131
150
|
|
132
151
|
private
|
133
152
|
|
153
|
+
# @!visibility private
|
154
|
+
def self.info_plist_exist?(app_bundle_path)
|
155
|
+
info_plist = File.join(app_bundle_path, "Info.plist")
|
156
|
+
File.exist?(info_plist)
|
157
|
+
end
|
158
|
+
|
159
|
+
# @!visibility private
|
160
|
+
def self.executable_file_exist?(app_bundle_path)
|
161
|
+
return false if !self.info_plist_exist?(app_bundle_path)
|
162
|
+
info_plist = File.join(app_bundle_path, "Info.plist")
|
163
|
+
pbuddy = RunLoop::PlistBuddy.new
|
164
|
+
name = pbuddy.plist_read("CFBundleExecutable", info_plist)
|
165
|
+
if name
|
166
|
+
File.exist?(File.join(app_bundle_path, name))
|
167
|
+
else
|
168
|
+
false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# @!visibility private
|
173
|
+
def lipo
|
174
|
+
@lipo ||= RunLoop::Lipo.new(path)
|
175
|
+
end
|
176
|
+
|
134
177
|
# @!visibility private
|
135
178
|
def plist_buddy
|
136
179
|
@plist_buddy ||= RunLoop::PlistBuddy.new
|
@@ -150,17 +193,20 @@ Bundle must:
|
|
150
193
|
|
151
194
|
# @!visibility private
|
152
195
|
def skip_executable_check?(file)
|
153
|
-
|
196
|
+
File.directory?(file) ||
|
197
|
+
image?(file) ||
|
154
198
|
text?(file) ||
|
155
199
|
plist?(file) ||
|
156
200
|
lproj_asset?(file) ||
|
157
201
|
code_signing_asset?(file) ||
|
158
|
-
core_data_asset?(file)
|
202
|
+
core_data_asset?(file) ||
|
203
|
+
font?(file)
|
159
204
|
end
|
160
205
|
|
161
206
|
# @!visibility private
|
162
207
|
def text?(file)
|
163
208
|
extension = File.extname(file)
|
209
|
+
filename = File.basename(file)
|
164
210
|
|
165
211
|
extension == ".txt" ||
|
166
212
|
extension == ".md" ||
|
@@ -170,12 +216,25 @@ Bundle must:
|
|
170
216
|
extension == ".yaml" ||
|
171
217
|
extension == ".yml" ||
|
172
218
|
extension == ".rtf" ||
|
173
|
-
|
219
|
+
|
220
|
+
["NOTICE", "LICENSE", "README", "ABOUT"].any? do |elm|
|
221
|
+
filename[/#{elm}/]
|
222
|
+
end
|
174
223
|
end
|
175
224
|
|
176
225
|
# @!visibility private
|
177
226
|
def image?(file)
|
178
|
-
file
|
227
|
+
extension = File.extname(file)
|
228
|
+
|
229
|
+
extension == ".jpeg" ||
|
230
|
+
extension == ".jpg" ||
|
231
|
+
extension == ".gif" ||
|
232
|
+
extension == ".png" ||
|
233
|
+
extension == ".tiff" ||
|
234
|
+
extension == ".svg" ||
|
235
|
+
extension == ".pdf" ||
|
236
|
+
extension == ".car" ||
|
237
|
+
file[/iTunesArtwork/, 0]
|
179
238
|
end
|
180
239
|
|
181
240
|
# @!visibility private
|
@@ -186,9 +245,11 @@ Bundle must:
|
|
186
245
|
# @!visibility private
|
187
246
|
def lproj_asset?(file)
|
188
247
|
extension = File.extname(file)
|
248
|
+
dir_extension = File.extname(File.dirname(file))
|
189
249
|
|
190
|
-
|
191
|
-
|
250
|
+
dir_extension == ".lproj" ||
|
251
|
+
dir_extension == ".storyboard" ||
|
252
|
+
dir_extension == ".storyboardc" ||
|
192
253
|
extension == ".strings" ||
|
193
254
|
extension == ".xib" ||
|
194
255
|
extension == ".nib"
|
@@ -198,21 +259,31 @@ Bundle must:
|
|
198
259
|
def code_signing_asset?(file)
|
199
260
|
name = File.basename(file)
|
200
261
|
extension = File.extname(file)
|
262
|
+
dirname = File.basename(File.dirname(file))
|
201
263
|
|
202
264
|
name == "PkgInfo" ||
|
203
265
|
name == "embedded" ||
|
204
266
|
extension == ".mobileprovision" ||
|
205
267
|
extension == ".xcent" ||
|
206
|
-
|
268
|
+
dirname == "_CodeSignature"
|
207
269
|
end
|
208
270
|
|
209
271
|
# @!visibility private
|
210
272
|
def core_data_asset?(file)
|
211
273
|
extension = File.extname(file)
|
274
|
+
dir_extension = File.extname(File.dirname(file))
|
212
275
|
|
213
|
-
|
276
|
+
dir_extension == ".momd" ||
|
214
277
|
extension == ".mom" ||
|
215
|
-
extension == ".db"
|
278
|
+
extension == ".db" ||
|
279
|
+
extension == ".omo"
|
280
|
+
end
|
281
|
+
|
282
|
+
# @!visibility private
|
283
|
+
def font?(file)
|
284
|
+
extension = File.extname(file)
|
285
|
+
|
286
|
+
extension == ".tff" || extension == ".otf"
|
216
287
|
end
|
217
288
|
end
|
218
289
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module DetectAUT
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class Detect
|
8
|
+
include RunLoop::DetectAUT::Errors
|
9
|
+
include RunLoop::DetectAUT::XamarinStudio
|
10
|
+
include RunLoop::DetectAUT::Xcode
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
def app_for_simulator
|
14
|
+
path = RunLoop::Environment.path_to_app_bundle
|
15
|
+
return RunLoop::App.new(path) if path
|
16
|
+
|
17
|
+
if xcode_project?
|
18
|
+
apps, search_dirs = detect_xcode_apps
|
19
|
+
elsif xamarin_project?
|
20
|
+
search_dirs = [solution_directory]
|
21
|
+
apps = candidate_apps(search_dirs.first)
|
22
|
+
else
|
23
|
+
apps = []
|
24
|
+
search_dirs = []
|
25
|
+
end
|
26
|
+
|
27
|
+
# If this is a Xamarin project, we've already searched the local
|
28
|
+
# directory tree for .app.
|
29
|
+
if apps.empty? && !xamarin_project?
|
30
|
+
search_dirs << File.expand_path("./")
|
31
|
+
apps = candidate_apps(File.expand_path("./"))
|
32
|
+
end
|
33
|
+
|
34
|
+
if apps.empty?
|
35
|
+
raise_no_simulator_app_found(search_dirs)
|
36
|
+
end
|
37
|
+
|
38
|
+
app = select_most_recent_app(apps)
|
39
|
+
|
40
|
+
RunLoop.log_info2("Detected app at path:")
|
41
|
+
RunLoop.log_info2("#{app.path}")
|
42
|
+
time_str = mtime(app).strftime("%a %d %b %Y %H:%M:%S %Z")
|
43
|
+
RunLoop.log_info2("Modification time of app: #{time_str}")
|
44
|
+
RunLoop.log_info2("If this is incorrect, set the APP variable and/or rebuild your app")
|
45
|
+
RunLoop.log_info2("It is your responsibility to ensure you are testing the right app.")
|
46
|
+
|
47
|
+
app
|
48
|
+
end
|
49
|
+
|
50
|
+
# @!visibility private
|
51
|
+
# @param [Array<RunLoop::Detect>] apps
|
52
|
+
def select_most_recent_app(apps)
|
53
|
+
apps.max do |a, b|
|
54
|
+
mtime(a).to_i <=> mtime(b).to_i
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @!visibility private
|
59
|
+
# @param [String] bundle_path
|
60
|
+
def app_with_bundle(bundle_path)
|
61
|
+
RunLoop::App.new(bundle_path)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!visibility private
|
65
|
+
# @param [String] base_dir where to start the recursive search
|
66
|
+
def candidate_apps(base_dir)
|
67
|
+
candidates = []
|
68
|
+
Dir.glob("#{base_dir}/**/*.app").each do |bundle_path|
|
69
|
+
app = app_or_nil(bundle_path)
|
70
|
+
candidates << app if app
|
71
|
+
end
|
72
|
+
candidates
|
73
|
+
end
|
74
|
+
|
75
|
+
# @!visibility private
|
76
|
+
# @param [String] bundle_path path to .app
|
77
|
+
def app_or_nil(bundle_path)
|
78
|
+
return nil if !RunLoop::App.valid?(bundle_path)
|
79
|
+
|
80
|
+
app = app_with_bundle(bundle_path)
|
81
|
+
if app.simulator? && app.calabash_server_version
|
82
|
+
app
|
83
|
+
else
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# @!visibility private
|
89
|
+
# @param [RunLoop::Detect] app
|
90
|
+
def mtime(app)
|
91
|
+
path = File.join(app.path, app.executable_name)
|
92
|
+
File.mtime(path)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
class SolutionMissingError < RuntimeError ; end
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class XcodeprojMissingError < RuntimeError; end
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
class MultipleXcodeprojError < RuntimeError; end
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
class NoSimulatorAppFoundError < RuntimeError; end
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
module DetectAUT
|
17
|
+
|
18
|
+
# @!visibility private
|
19
|
+
module Errors
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
#
|
23
|
+
# Raised when XCODEPROJ is defined, but does not exist
|
24
|
+
def raise_xcodeproj_missing(xcodeproj)
|
25
|
+
raise RunLoop::XcodeprojMissingError,
|
26
|
+
%Q[The XCODEPROJ environment variable has been set to:
|
27
|
+
|
28
|
+
#{xcodeproj}
|
29
|
+
|
30
|
+
but no directory exists at that path.
|
31
|
+
|
32
|
+
You should only set XCODEPROJ variable if your Calabash project has more than
|
33
|
+
one .xcodeproj directory or your .xcodeproj directory is located above the
|
34
|
+
current directory (where you run `cucumber` from). Calabash will discover the
|
35
|
+
.xcodeproj if is below the current working directory.
|
36
|
+
|
37
|
+
# .xcodeproj is above the working directory
|
38
|
+
$ ls ../*.xcodeproj
|
39
|
+
MyApp.xcodeproj
|
40
|
+
|
41
|
+
$ XCODEPROJ=../*.xcodeproj cucumber
|
42
|
+
|
43
|
+
# There is more than one .xcodeproj
|
44
|
+
$ ls *.xcodeproj
|
45
|
+
MyiOSApp.xcodeproj
|
46
|
+
MyMacApp.xcodeproj
|
47
|
+
|
48
|
+
$ XCODEPROJ=MyiOSApp.xcodeproj
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
# @!visibility private
|
53
|
+
#
|
54
|
+
# Raised when there are more than > 1 .xcodeproj in **/*
|
55
|
+
# Is not raised when there are 0 .xcodeproj in **/*
|
56
|
+
#
|
57
|
+
# @param [Array<String>] projects a list of paths to .xcodeproj
|
58
|
+
def raise_multiple_xcodeproj(projects)
|
59
|
+
raise RunLoop::MultipleXcodeprojError,
|
60
|
+
%Q[Found multiple .xcodeproj directories:
|
61
|
+
|
62
|
+
#{projects.each { |path| puts " #{path}" }}
|
63
|
+
|
64
|
+
Which project contains the target of the application you are trying to test?
|
65
|
+
|
66
|
+
Set the XCODEPROJ variable to specify the correct .xcodeproj directory.
|
67
|
+
|
68
|
+
# Examples
|
69
|
+
$ XCODEPROJ="../MyApp.xcodeproj" cucumber
|
70
|
+
$ XCODEPROJ="iOS/MyApp.xcodeproj" cucumber
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
74
|
+
# @!visibility private
|
75
|
+
#
|
76
|
+
# Raised when SOLUTION is defined but does not exist
|
77
|
+
def raise_solution_missing(solution)
|
78
|
+
raise RunLoop::SolutionMissingError,
|
79
|
+
%Q[The SOLUTION environment variable has been set to:
|
80
|
+
|
81
|
+
#{solution}
|
82
|
+
|
83
|
+
but no solution exists at that path.
|
84
|
+
|
85
|
+
You should only set SOLUTION variable if your .sln is not located in
|
86
|
+
the current directory or in the directory just above the current
|
87
|
+
directory (where you run `cucumber` from). Calabash will discover the
|
88
|
+
.snl if it is in the current working directory or the directory just
|
89
|
+
above.
|
90
|
+
|
91
|
+
# Calabash will discover solution - don't set SOLUTION
|
92
|
+
$ ls *.sln => MyApp.sln
|
93
|
+
$ ls ../*.sln => MyApp.sln
|
94
|
+
|
95
|
+
# Calabash will _not_ discover solution
|
96
|
+
$ ls ../../*.sln => MyApp.sln
|
97
|
+
$ SOLUTION=../../MyApp.sln cucumber
|
98
|
+
|
99
|
+
$ ls project/*.sln => MyApp.sln
|
100
|
+
$ SOLUTION=project/MyApp.sln cucumber
|
101
|
+
|
102
|
+
$ ls ~/some/other/directory/*.sln => MyApp.sln
|
103
|
+
$ SOLUTION=~/some/other/directory/MyApp.sln
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
# @!visibility private
|
108
|
+
# Raised when no app can found by the discovery algorithm
|
109
|
+
def raise_no_simulator_app_found(search_directories)
|
110
|
+
raise RunLoop::NoSimulatorAppFoundError,
|
111
|
+
%Q[Searched these directories:
|
112
|
+
|
113
|
+
#{search_directories.each { |path| puts " #{path}" }}
|
114
|
+
|
115
|
+
but could not find any .app for the simulator that contains the Calabash iOS
|
116
|
+
server.
|
117
|
+
|
118
|
+
Make sure you have built your app for a simulator target from Xcode.
|
119
|
+
|
120
|
+
If you testing a stand-alone .app (you don't have an Xcode project), put your
|
121
|
+
.app in the same directory (or below) the directory you run `cucumber` from.
|
122
|
+
]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module RunLoop
|
2
|
+
# @!visibility private
|
3
|
+
module DetectAUT
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
module XamarinStudio
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
def xamarin_project?
|
10
|
+
solution_directory != nil
|
11
|
+
end
|
12
|
+
|
13
|
+
# @!visibility private
|
14
|
+
def solution_directory
|
15
|
+
solution = RunLoop::Environment.solution
|
16
|
+
|
17
|
+
if solution && !File.exist?(solution)
|
18
|
+
raise_solution_missing(solution)
|
19
|
+
end
|
20
|
+
|
21
|
+
# SOLUTION defined and exists
|
22
|
+
return File.dirname(solution) if solution
|
23
|
+
|
24
|
+
solution_dir = find_solution_directory
|
25
|
+
return nil if solution_dir.nil?
|
26
|
+
|
27
|
+
solution_dir
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!visibility private
|
31
|
+
def find_solution_directory
|
32
|
+
pwd = Dir.pwd
|
33
|
+
solutions = Dir.glob("#{pwd}/*.sln")
|
34
|
+
|
35
|
+
if solutions.empty?
|
36
|
+
solutions = Dir.glob("#{pwd}/../*.sln")
|
37
|
+
end
|
38
|
+
|
39
|
+
return nil if solutions.empty?
|
40
|
+
|
41
|
+
File.expand_path(File.dirname(solutions.first))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module RunLoop
|
2
|
+
# @!visibility private
|
3
|
+
module DetectAUT
|
4
|
+
# @!visibility private
|
5
|
+
module Xcode
|
6
|
+
|
7
|
+
# @!visibility private
|
8
|
+
def xcode_project?
|
9
|
+
xcodeproj != nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
def xcodeproj
|
14
|
+
xcodeproj = RunLoop::Environment.xcodeproj
|
15
|
+
|
16
|
+
if xcodeproj && !File.directory?(xcodeproj)
|
17
|
+
raise_xcodeproj_missing(xcodeproj)
|
18
|
+
end
|
19
|
+
|
20
|
+
# XCODEPROJ defined and exists
|
21
|
+
return xcodeproj if xcodeproj
|
22
|
+
|
23
|
+
projects = find_xcodeproj
|
24
|
+
|
25
|
+
return nil if projects.empty?
|
26
|
+
return projects[0] if projects.count == 1
|
27
|
+
|
28
|
+
raise_multiple_xcodeproj(projects)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!visibility private
|
32
|
+
def find_xcodeproj
|
33
|
+
Dir.glob("#{Dir.pwd}/**/*.xcodeproj")
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def detect_xcode_apps
|
38
|
+
dirs_to_search = derived_data_search_dirs
|
39
|
+
|
40
|
+
dir_from_prefs = xcode_preferences_search_dir
|
41
|
+
if dir_from_prefs
|
42
|
+
dirs_to_search << dir_from_prefs
|
43
|
+
end
|
44
|
+
|
45
|
+
apps = []
|
46
|
+
dirs_to_search.each do |dir|
|
47
|
+
# defined in detect_aut/apps.rb
|
48
|
+
candidates = candidate_apps(dir)
|
49
|
+
apps = apps.concat(candidates)
|
50
|
+
end
|
51
|
+
|
52
|
+
return apps, dirs_to_search
|
53
|
+
end
|
54
|
+
|
55
|
+
# @!visibility private
|
56
|
+
PLIST_KEYS = {
|
57
|
+
:workspace => "WorkspacePath",
|
58
|
+
:shared_build => "IDESharedBuildFolderName",
|
59
|
+
:custom_build => "IDECustomBuildProductsPath"
|
60
|
+
}
|
61
|
+
|
62
|
+
# @!visibility private
|
63
|
+
# TODO Needs unit tests
|
64
|
+
def derived_data_search_dirs
|
65
|
+
project = xcodeproj
|
66
|
+
project_name = File.basename(project)
|
67
|
+
matches = []
|
68
|
+
|
69
|
+
# WorkspacePath could be a .xcodeproj or .xcworkspace
|
70
|
+
#
|
71
|
+
# # Exact match.
|
72
|
+
# xcodeproj = path/to/MyApp/MyApp.xcodeproj
|
73
|
+
# WorkspacePath = path/to/MyApp/MyApp.xcodeproj
|
74
|
+
#
|
75
|
+
# # CocoaPods projects are often configured like this. As are legacy
|
76
|
+
# # projects that have been added to a new workspace.
|
77
|
+
# xcodeproj = path/to/MyApp/MyApp.xcodeproj
|
78
|
+
# WorkspacePath = path/to/MyApp/MyApp.xcworkspace
|
79
|
+
#
|
80
|
+
# # This is the Xcode default when creating new iOS project
|
81
|
+
# xcodeproj = path/to/MyApp/MyApp/MyApp.xcodeproj
|
82
|
+
# WorkspacePath = path/to/MyApp/MyApp.xcworkspace
|
83
|
+
key = PLIST_KEYS[:workspace]
|
84
|
+
Dir.glob("#{derived_data}/*/info.plist") do |plist|
|
85
|
+
workspace = pbuddy.plist_read(key, plist)
|
86
|
+
if workspace == project
|
87
|
+
matches << File.dirname(plist)
|
88
|
+
else
|
89
|
+
base_dir = File.dirname(workspace)
|
90
|
+
if File.exist?(File.join(base_dir, project_name))
|
91
|
+
matches << File.dirname(plist)
|
92
|
+
elsif !Dir.glob("#{File.dirname(workspace)}/*/#{project_name}").empty?
|
93
|
+
matches << File.dirname(plist)
|
94
|
+
elsif !Dir.glob("#{File.dirname(workspace)}/**/#{project_name}").empty?
|
95
|
+
matches << File.dirname(plist)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
matches
|
100
|
+
end
|
101
|
+
|
102
|
+
# @!visibility private
|
103
|
+
def xcode_preferences_search_dir
|
104
|
+
plist = xcode_preferences_plist
|
105
|
+
|
106
|
+
shared_build_folder = pbuddy.plist_read(PLIST_KEYS[:shared_build], plist)
|
107
|
+
custom_build_folder = pbuddy.plist_read(PLIST_KEYS[:custom_build], plist)
|
108
|
+
|
109
|
+
if shared_build_folder
|
110
|
+
File.join(derived_data, shared_build_folder, "Products")
|
111
|
+
elsif custom_build_folder
|
112
|
+
File.join(File.dirname(xcodeproj), custom_build_folder)
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @!visibility private
|
119
|
+
def derived_data
|
120
|
+
RunLoop::Environment.derived_data ||
|
121
|
+
File.expand_path("~/Library/Developer/Xcode/DerivedData")
|
122
|
+
end
|
123
|
+
|
124
|
+
# @!visibility private
|
125
|
+
def xcode_preferences_plist
|
126
|
+
File.expand_path('~/Library/Preferences/com.apple.dt.Xcode.plist')
|
127
|
+
end
|
128
|
+
|
129
|
+
# @!visibility private
|
130
|
+
def pbuddy
|
131
|
+
@pbuddy ||= RunLoop::PlistBuddy.new
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
data/lib/run_loop/environment.rb
CHANGED
@@ -38,6 +38,49 @@ module RunLoop
|
|
38
38
|
ENV["DEVICE_ENDPOINT"]
|
39
39
|
end
|
40
40
|
|
41
|
+
# Returns the value of XCODEPROJ which can be used to specify an Xcode
|
42
|
+
# project directory (my.xcodeproj).
|
43
|
+
#
|
44
|
+
# This is useful if your project has multiple xcodeproj directories.
|
45
|
+
#
|
46
|
+
# Most users should not set this variable.
|
47
|
+
def self.xcodeproj
|
48
|
+
value = ENV["XCODEPROJ"]
|
49
|
+
if value.nil? || value == ""
|
50
|
+
return nil
|
51
|
+
else
|
52
|
+
File.expand_path(value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the value of DERIVED_DATA which can be used to specify an
|
57
|
+
# alternative DerivedData directory.
|
58
|
+
#
|
59
|
+
# The default is ~/Library/Xcode/DerivedData, but Xcode allows you to
|
60
|
+
# change this value.
|
61
|
+
def self.derived_data
|
62
|
+
value = ENV["DERIVED_DATA"]
|
63
|
+
if value.nil? || value == ""
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
File.expand_path(value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the value of SOLUTION which can be used to specify a
|
71
|
+
# Xamarin Studio .sln
|
72
|
+
#
|
73
|
+
# This is useful if your project has multiple solutions (.sln)
|
74
|
+
# and Calabash cannot detect the correct one.
|
75
|
+
def self.solution
|
76
|
+
value = ENV["SOLUTION"]
|
77
|
+
if value.nil? || value == ""
|
78
|
+
nil
|
79
|
+
else
|
80
|
+
File.expand_path(value)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
41
84
|
# Returns the value of TRACE_TEMPLATE; the Instruments template to use
|
42
85
|
# during testing.
|
43
86
|
def self.trace_template
|
data/lib/run_loop/ipa.rb
CHANGED
@@ -45,6 +45,11 @@ module RunLoop
|
|
45
45
|
app.executable_name
|
46
46
|
end
|
47
47
|
|
48
|
+
# Returns the arches for the binary.
|
49
|
+
def arches
|
50
|
+
app.arches
|
51
|
+
end
|
52
|
+
|
48
53
|
# Inspects the app's executables for the server version
|
49
54
|
# @return[RunLoop::Version] a version instance
|
50
55
|
def calabash_server_version
|
data/lib/run_loop/otool.rb
CHANGED
data/lib/run_loop/version.rb
CHANGED
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.8
|
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-
|
11
|
+
date: 2016-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -296,6 +296,10 @@ files:
|
|
296
296
|
- lib/run_loop/codesign.rb
|
297
297
|
- lib/run_loop/core.rb
|
298
298
|
- lib/run_loop/core_simulator.rb
|
299
|
+
- lib/run_loop/detect_aut/detect.rb
|
300
|
+
- lib/run_loop/detect_aut/errors.rb
|
301
|
+
- lib/run_loop/detect_aut/xamarin_studio.rb
|
302
|
+
- lib/run_loop/detect_aut/xcode.rb
|
299
303
|
- lib/run_loop/device.rb
|
300
304
|
- lib/run_loop/directory.rb
|
301
305
|
- lib/run_loop/dot_dir.rb
|
@@ -369,3 +373,4 @@ specification_version: 4
|
|
369
373
|
summary: The bridge between Calabash iOS and Xcode command-line tools like instruments
|
370
374
|
and simctl.
|
371
375
|
test_files: []
|
376
|
+
has_rdoc:
|