run_loop 2.0.5 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e74f558ad6dac0b1679f8d2cdd4566810aa922fd
4
- data.tar.gz: a81469d91af4d38631e344de86e8176f85b66b06
3
+ metadata.gz: e713b8074f4a98cc789fe39327c63c675d8483cb
4
+ data.tar.gz: 98d467c958129c5a33efa0fe654088f38b0b7b0f
5
5
  SHA512:
6
- metadata.gz: 512cf1830f6ee0a79e00879ad72c38c2f20563867128995652c58dc2d6f34d5200a289f5d08c148c8b16bc8f293409be0e3b3cae965601855caa481e33cd7eaa
7
- data.tar.gz: 110fc79bc875f364d9cf3a1b020467c0649a3d39e7c4f2c701bfb0c812d352d399bc3ce3561df0a83000b941044d7bdb0e8762f9c71f2123189c40245456ae75
6
+ metadata.gz: 810a32b9c38f9b720129ca06dc8a80cb6d92c4243d8bd7dc4dd62cee55600e19d68f9eb38d7c28643f9ed023d3437e1c9b35beb26a1e9f95a64b8de3a9308a23
7
+ data.tar.gz: 0963ce5adff45b9aae3b20cc2635000ea69ef166deef85956461c2808abca49a371140d8a42d911a622a4e88547fbd6e90c713ff63befeaf49087b921ecfb56a
@@ -20,6 +20,8 @@ require 'run_loop/sim_control'
20
20
  require 'run_loop/device'
21
21
  require 'run_loop/instruments'
22
22
  require 'run_loop/lipo'
23
+ require "run_loop/otool"
24
+ require "run_loop/strings"
23
25
  require 'run_loop/cache/cache'
24
26
  require 'run_loop/host_cache'
25
27
  require 'run_loop/patches/awesome_print'
@@ -14,6 +14,20 @@ module RunLoop
14
14
  # @return [RunLoop::App] A instance of App with a path.
15
15
  def initialize(app_bundle_path)
16
16
  @path = File.expand_path(app_bundle_path)
17
+
18
+ if !App.valid?(app_bundle_path)
19
+ raise ArgumentError,
20
+ %Q{App does not exist at path or is not an app bundle.
21
+
22
+ #{app_bundle_path}
23
+
24
+ Bundle must:
25
+
26
+ 1. be a directory that exists,
27
+ 2. have a .app extension,
28
+ 3. and contain an Info.plist.
29
+ }
30
+ end
17
31
  end
18
32
 
19
33
  # @!visibility private
@@ -28,19 +42,23 @@ module RunLoop
28
42
 
29
43
  # Is this a valid app?
30
44
  def valid?
31
- [File.exist?(path),
32
- File.directory?(path),
33
- File.extname(path) == '.app'].all?
45
+ App.valid?(path)
46
+ end
47
+
48
+ # @!visibility private
49
+ def self.valid?(app_bundle_path)
50
+ return false if app_bundle_path.nil?
51
+
52
+ File.exist?(app_bundle_path) &&
53
+ File.directory?(app_bundle_path) &&
54
+ File.extname(app_bundle_path) == '.app' &&
55
+ File.exist?(File.join(app_bundle_path, "Info.plist"))
34
56
  end
35
57
 
36
58
  # Returns the Info.plist path.
37
59
  # @raise [RuntimeError] If there is no Info.plist.
38
60
  def info_plist_path
39
- info_plist = File.join(path, 'Info.plist')
40
- unless File.exist?(info_plist)
41
- raise "Expected an Info.plist at '#{path}'"
42
- end
43
- info_plist
61
+ @info_plist_path ||= File.join(path, 'Info.plist')
44
62
  end
45
63
 
46
64
  # Inspects the app's Info.plist for the bundle identifier.
@@ -69,20 +87,26 @@ module RunLoop
69
87
 
70
88
  # Inspects the app's file for the server version
71
89
  def calabash_server_version
72
- if valid?
73
- path_to_bin = File.join(path, executable_name)
74
- xcrun ||= RunLoop::Xcrun.new
75
- hash = xcrun.exec(["strings", path_to_bin])
76
- unless hash.nil?
77
- version_str = hash[:out][/CALABASH VERSION: \d+\.\d+\.\d+/, 0]
78
- unless version_str.nil? || version_str == ""
79
- server_ver = version_str.split(":")[1].delete(' ')
80
- RunLoop::Version.new(server_ver)
81
- end
90
+ version = nil
91
+ executables.each do |executable|
92
+ version = strings(executable).server_version
93
+ break if version
94
+ end
95
+ version
96
+ end
97
+
98
+ # @!visibility private
99
+ # Collects the paths to executables in the bundle.
100
+ def executables
101
+ executables = []
102
+ Dir.glob("#{path}/**/*") do |file|
103
+ next if File.directory?(file)
104
+ next if skip_executable_check?(file)
105
+ if otool(file).executable?
106
+ executables << file
82
107
  end
83
- else
84
- raise 'Path is not valid'
85
108
  end
109
+ executables
86
110
  end
87
111
 
88
112
  # Returns the sha1 of the application.
@@ -92,9 +116,89 @@ module RunLoop
92
116
 
93
117
  private
94
118
 
119
+ # @!visibility private
95
120
  def plist_buddy
96
121
  @plist_buddy ||= RunLoop::PlistBuddy.new
97
122
  end
98
123
 
124
+ # @!visibility private
125
+ # An otool factory.
126
+ def otool(file)
127
+ RunLoop::Otool.new(file)
128
+ end
129
+
130
+ # @!visibility private
131
+ # A strings factory
132
+ def strings(file)
133
+ RunLoop::Strings.new(file)
134
+ end
135
+
136
+ # @!visibility private
137
+ def skip_executable_check?(file)
138
+ image?(file) ||
139
+ text?(file) ||
140
+ plist?(file) ||
141
+ lproj_asset?(file) ||
142
+ code_signing_asset?(file) ||
143
+ core_data_asset?(file)
144
+ end
145
+
146
+ # @!visibility private
147
+ def text?(file)
148
+ extension = File.extname(file)
149
+
150
+ extension == ".txt" ||
151
+ extension == ".md" ||
152
+ extension == ".html" ||
153
+ extension == ".xml" ||
154
+ extension == ".json" ||
155
+ extension == ".yaml" ||
156
+ extension == ".yml" ||
157
+ extension == ".rtf" ||
158
+ file[/NOTICE|LICENSE|README|ABOUT/, 0]
159
+ end
160
+
161
+ # @!visibility private
162
+ def image?(file)
163
+ file[/jpeg|jpg|gif|png|tiff|svg|pdf|car|iTunesArtwork/, 0]
164
+ end
165
+
166
+ # @!visibility private
167
+ def plist?(file)
168
+ File.extname(file) == ".plist"
169
+ end
170
+
171
+ # @!visibility private
172
+ def lproj_asset?(file)
173
+ extension = File.extname(file)
174
+
175
+ file[/lproj/, 0] ||
176
+ file[/storyboard/, 0] ||
177
+ extension == ".strings" ||
178
+ extension == ".xib" ||
179
+ extension == ".nib"
180
+ end
181
+
182
+ # @!visibility private
183
+ def code_signing_asset?(file)
184
+ name = File.basename(file)
185
+ extension = File.extname(file)
186
+
187
+ name == "PkgInfo" ||
188
+ name == "embedded" ||
189
+ extension == ".mobileprovision" ||
190
+ extension == ".xcent" ||
191
+ file[/_CodeSignature/, 0]
192
+ end
193
+
194
+ # @!visibility private
195
+ def core_data_asset?(file)
196
+ extension = File.extname(file)
197
+
198
+ file[/momd/, 0] ||
199
+ extension == ".mom" ||
200
+ extension == ".db"
201
+ end
99
202
  end
100
203
  end
204
+
@@ -2,18 +2,11 @@ module RunLoop
2
2
  # A model of the an .ipa - a application binary for iOS devices.
3
3
  class Ipa
4
4
 
5
-
6
5
  # The path to this .ipa.
7
6
  # @!attribute [r] path
8
7
  # @return [String] A path to this .ipa.
9
8
  attr_reader :path
10
9
 
11
- # The bundle identifier of this ipa.
12
- # @!attribute [r] bundle_identifier
13
- # @return [String] The bundle identifier of this ipa; obtained by inspecting
14
- # the app's Info.plist.
15
- attr_reader :bundle_identifier
16
-
17
10
  # Create a new ipa instance.
18
11
  # @param [String] path_to_ipa The path the .ipa file.
19
12
  # @return [Calabash::Ipa] A new ipa instance.
@@ -42,96 +35,59 @@ module RunLoop
42
35
 
43
36
  # The bundle identifier of this ipa.
44
37
  # @return [String] A string representation of this ipa's CFBundleIdentifier
45
- # @raise [RuntimeError] If ipa does not expand into a Payload/<app name>.app
46
- # directory.
47
- # @raise [RuntimeError] If an Info.plist does exist in the .app.
48
38
  def bundle_identifier
49
- if bundle_dir.nil? || !File.exist?(bundle_dir)
50
- raise "Expected a '#{File.basename(path).split('.').first}.app'\nat path '#{payload_dir}'"
51
- end
52
-
53
- @bundle_identifier ||= lambda {
54
- info_plist_path = File.join(bundle_dir, 'Info.plist')
55
- unless File.exist? info_plist_path
56
- raise "Expected an 'Info.plist' at '#{bundle_dir}'"
57
- end
58
- identifier = plist_buddy.plist_read('CFBundleIdentifier', info_plist_path)
59
-
60
- unless identifier
61
- raise "Expected key 'CFBundleIdentifier' in '#{info_plist_path}'"
62
- end
63
- identifier
64
- }.call
39
+ app.bundle_identifier
65
40
  end
66
41
 
67
42
  # Inspects the app's Info.plist for the executable name.
68
43
  # @return [String] The value of CFBundleExecutable.
69
- # @raise [RuntimeError] If the plist cannot be read or the
70
- # CFBundleExecutable is empty or does not exist.
71
44
  def executable_name
72
- if bundle_dir.nil? || !File.exist?(bundle_dir)
73
- raise "Expected a '#{File.basename(path).split('.').first}.app'\nat path '#{payload_dir}'"
74
- end
75
-
76
- @executable_name ||= lambda {
77
- info_plist_path = File.join(bundle_dir, 'Info.plist')
78
- unless File.exist? info_plist_path
79
- raise "Expected an 'Info.plist' at '#{bundle_dir}'"
80
- end
81
- name = plist_buddy.plist_read('CFBundleExecutable', info_plist_path)
82
-
83
- unless name
84
- raise "Expected key 'CFBundleExecutable' in '#{info_plist_path}'"
85
- end
86
- name
87
- }.call
45
+ app.executable_name
88
46
  end
89
47
 
90
- # Inspects the app's file for the server version
48
+ # Inspects the app's executables for the server version
49
+ # @return[RunLoop::Version] a version instance
91
50
  def calabash_server_version
92
- if bundle_dir.nil? || !File.exist?(bundle_dir)
93
- raise "Expected a '#{File.basename(path).split('.').first}.app'\nat path '#{payload_dir}'"
94
- else
95
- if !executable_name.nil? && executable_name != ''
96
- path_to_bin = File.join(bundle_dir, executable_name)
97
- xcrun ||= RunLoop::Xcrun.new
98
- hash = xcrun.exec(["strings", path_to_bin])
99
- unless hash.nil?
100
- version_str = hash[:out][/CALABASH VERSION: \d+\.\d+\.\d+/, 0]
101
- unless version_str.nil? || version_str == ""
102
- server_ver = version_str.split(":")[1].delete(' ')
103
- RunLoop::Version.new(server_ver)
104
- end
105
- end
106
- end
107
- end
51
+ app.calabash_server_version
108
52
  end
109
53
 
110
54
  private
111
55
 
56
+ # @!visibility private
112
57
  def tmpdir
113
58
  @tmpdir ||= Dir.mktmpdir
114
59
  end
115
60
 
61
+ # @!visibility private
116
62
  def payload_dir
117
- @payload_dir ||= lambda {
63
+ @payload_dir ||= lambda do
118
64
  FileUtils.cp(path, tmpdir)
119
65
  zip_path = File.join(tmpdir, File.basename(path))
120
66
  Dir.chdir(tmpdir) do
121
67
  system('unzip', *['-q', zip_path])
122
68
  end
123
69
  File.join(tmpdir, 'Payload')
124
- }.call
70
+ end.call
125
71
  end
126
72
 
73
+ # @!visibility private
127
74
  def bundle_dir
128
- @bundle_dir ||= lambda {
129
- Dir.glob(File.join(payload_dir, '*')).detect {|f| File.directory?(f) && f.end_with?('.app')}
130
- }.call
75
+ @bundle_dir ||= lambda do
76
+ Dir.glob(File.join(payload_dir, '*')).detect do |f|
77
+ File.directory?(f) && f.end_with?('.app')
78
+ end
79
+ end.call
80
+ end
81
+
82
+ # @!visibility private
83
+ def app
84
+ @app ||= RunLoop::App.new(bundle_dir)
131
85
  end
132
86
 
87
+ # @!visibility private
133
88
  def plist_buddy
134
89
  @plist_buddy ||= RunLoop::PlistBuddy.new
135
90
  end
136
91
  end
137
92
  end
93
+
@@ -0,0 +1,76 @@
1
+ module RunLoop
2
+ # @!visibility private
3
+ #
4
+ # A class for interacting with otool
5
+ class Otool
6
+
7
+ # @!visibility private
8
+ attr_reader :path
9
+
10
+ # @!visibility private
11
+ def initialize(path)
12
+ @path = path
13
+
14
+ if !Otool.valid_path?(path)
15
+ raise ArgumentError,
16
+ %Q{File:
17
+
18
+ #{path}
19
+
20
+ must exist and not be a directory.
21
+ }
22
+ end
23
+ end
24
+
25
+ # @!visibility private
26
+ def to_s
27
+ "#<OTOOL: #{path}>"
28
+ end
29
+
30
+ # @!visibility private
31
+ def inspect
32
+ to_s
33
+ end
34
+
35
+ # @!visibility private
36
+ def executable?
37
+ !arch_info[/is not an object file/, 0]
38
+ end
39
+
40
+ private
41
+
42
+ # @!visibility private
43
+ def arch_info
44
+ args = ["otool", "-hv", "-arch", "all", path]
45
+ opts = { :log_cmd => true }
46
+
47
+ hash = xcrun.exec(args, opts)
48
+
49
+ if hash[:exit_status] != 0
50
+ raise RuntimeError,
51
+ %Q{Could not get arch info from file:
52
+
53
+ #{path}
54
+
55
+ #{args.join(" ")}
56
+
57
+ exited #{hash[:exit_status]} with the following output:
58
+
59
+ #{hash[:out]}
60
+ }
61
+ end
62
+
63
+ @arch_info = hash[:out]
64
+ end
65
+
66
+ # @!visibility private
67
+ def self.valid_path?(path)
68
+ File.exist?(path) && !File.directory?(path)
69
+ end
70
+
71
+ # @!visibility private
72
+ def xcrun
73
+ RunLoop::Xcrun.new
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,88 @@
1
+ module RunLoop
2
+ # @!visibility private
3
+ #
4
+ # A class for interacting with the strings tool
5
+ class Strings
6
+
7
+ # @!visibility private
8
+ attr_reader :path
9
+
10
+ # @!visibility private
11
+ def initialize(path)
12
+ @path = path
13
+
14
+ if !Strings.valid_path?(path)
15
+ raise ArgumentError,
16
+ %Q{File:
17
+
18
+ #{path}
19
+
20
+ must exist and not be a directory.
21
+ }
22
+ end
23
+ end
24
+
25
+ # @!visibility private
26
+ def to_s
27
+ "#<STRINGS: #{path}>"
28
+ end
29
+
30
+ # @!visibility private
31
+ def inspect
32
+ to_s
33
+ end
34
+
35
+ # @!visibility private
36
+ #
37
+ # @return [RunLoop::Version] A version instance or nil if the file
38
+ # at path does not contain server version information.
39
+ def server_version
40
+ regex = /CALABASH VERSION: (\d+\.\d+\.\d+(\.pre\d+)?)/
41
+ match = dump[regex, 0]
42
+
43
+ if match
44
+ str = match.split(":")[1]
45
+ RunLoop::Version.new(str)
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # @!visibility private
54
+ def dump
55
+ args = ["strings", path]
56
+ opts = { :log_cmd => true }
57
+
58
+ hash = xcrun.exec(args, opts)
59
+
60
+ if hash[:exit_status] != 0
61
+ raise RuntimeError,
62
+ %Q{Could not get strings info from file:
63
+
64
+ #{path}
65
+
66
+ #{args.join(" ")}
67
+
68
+ exited #{hash[:exit_status]} with the following output:
69
+
70
+ #{hash[:out]}
71
+ }
72
+ end
73
+
74
+ @dump = hash[:out]
75
+ end
76
+
77
+ # @!visibility private
78
+ def self.valid_path?(path)
79
+ File.exist?(path) && !File.directory?(path)
80
+ end
81
+
82
+ # @!visibility private
83
+ def xcrun
84
+ RunLoop::Xcrun.new
85
+ end
86
+ end
87
+ end
88
+
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = "2.0.5"
2
+ VERSION = "2.0.6"
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
@@ -21,6 +21,8 @@ module RunLoop
21
21
  # Raised when Xcrun fails.
22
22
  class Error < RuntimeError; end
23
23
 
24
+ # Raised when the output of the command cannot be coerced to UTF8
25
+ class UTF8Error < RuntimeError; end
24
26
 
25
27
  # Raised when Xcrun times out.
26
28
  class TimeoutError < RuntimeError; end
@@ -58,12 +60,7 @@ IO.popen requires all arguments to be Strings.
58
60
  start_time = Time.now
59
61
  command_output = CommandRunner.run(['xcrun'] + args, timeout: timeout)
60
62
 
61
- if command_output[:out]
62
- out = command_output[:out].force_encoding('utf-8').chomp
63
- else
64
- out = ''
65
- end
66
-
63
+ out = encode_utf8_or_raise(command_output[:out], cmd)
67
64
  process_status = command_output[:status]
68
65
 
69
66
  hash =
@@ -74,6 +71,8 @@ IO.popen requires all arguments to be Strings.
74
71
  :exit_status => process_status.exitstatus
75
72
  }
76
73
 
74
+ rescue UTF8Error => e
75
+ raise e
77
76
  rescue => e
78
77
  elapsed = "%0.2f" % (Time.now - start_time)
79
78
  raise Error,
@@ -100,5 +99,35 @@ with a timeout of #{timeout}
100
99
 
101
100
  hash
102
101
  end
102
+
103
+ private
104
+
105
+ # @!visibility private
106
+ def encode_utf8_or_raise(string, command)
107
+ return '' if !string
108
+
109
+ utf8 = string.force_encoding("UTF-8").chomp
110
+
111
+ return utf8 if utf8.valid_encoding?
112
+
113
+ encoded = utf8.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '')
114
+
115
+ return encoded if encoded.valid_encoding?
116
+
117
+ raise UTF8Error, %Q{
118
+ Could not force UTF-8 encoding on this string:
119
+
120
+ #{string}
121
+
122
+ which is the output of this command:
123
+
124
+ #{command}
125
+
126
+ Please file an issue with a stacktrace and the text of this error.
127
+
128
+ https://github.com/calabash/run_loop/issues
129
+ }
130
+ end
103
131
  end
104
132
  end
133
+
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.5
4
+ version: 2.0.6
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-01-29 00:00:00.000000000 Z
11
+ date: 2016-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -295,6 +295,7 @@ files:
295
295
  - lib/run_loop/lldb.rb
296
296
  - lib/run_loop/locale.rb
297
297
  - lib/run_loop/logging.rb
298
+ - lib/run_loop/otool.rb
298
299
  - lib/run_loop/patches/awesome_print.rb
299
300
  - lib/run_loop/plist_buddy.rb
300
301
  - lib/run_loop/process_terminator.rb
@@ -302,6 +303,7 @@ files:
302
303
  - lib/run_loop/regex.rb
303
304
  - lib/run_loop/sim_control.rb
304
305
  - lib/run_loop/simctl/plists.rb
306
+ - lib/run_loop/strings.rb
305
307
  - lib/run_loop/template.rb
306
308
  - lib/run_loop/version.rb
307
309
  - lib/run_loop/xcode.rb