run_loop 1.1.1.pre7 → 1.1.1.pre8

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: 1420a614a1d2f74b9cbb6e7bf376fd1521eebd01
4
- data.tar.gz: 26965697486e6081811a4f876e333d0d77f4750e
3
+ metadata.gz: 9340394da27267a6ea43ce6a61c2cb018faf97ff
4
+ data.tar.gz: 54d9b6db6739d00d5895b7d6108b8fd374f51aa1
5
5
  SHA512:
6
- metadata.gz: 805cfd4180e6566a36e4b518c102a10dd5ce93f2e405d14db8d13904e9ef6425aeaf2b64d316b963b718492250cf1582b8dc4f82bc487fe6124dc40689151d7f
7
- data.tar.gz: a43ef171976bb68f1ed6b19a582a29b1fec64dcfe6cbdaf3a710c229a941bf6c18ff091948aabdc74a2af21fcee7f984a01fe7ba180a9a9bbf39aa814b019921
6
+ metadata.gz: 9cbcdcee2fe866606caff3ec4609375777617a2de2ea216f314662539b2addda8ae42562bba6b624355a8398500568e442f7f8ac4980cd96e08dccfd047b54cf
7
+ data.tar.gz: 83bb516e414e2e1c05a1cc63f45e32194c2dbc3359c52894a406f48fd9b93b759f3adc5aef026060d0a4a66c5f8900a24e5be2b183a2f9c055c1c120eda3f8b2
data/lib/run_loop.rb CHANGED
@@ -5,3 +5,5 @@ require 'run_loop/plist_buddy'
5
5
  require 'run_loop/sim_control'
6
6
  require 'run_loop/device'
7
7
  require 'run_loop/instruments'
8
+ require 'run_loop/lipo'
9
+
data/lib/run_loop/core.rb CHANGED
@@ -83,6 +83,48 @@ module RunLoop
83
83
  nil
84
84
  end
85
85
 
86
+ # Raise an error if the application binary is not compatible with the
87
+ # target simulator.
88
+ #
89
+ # @note This method is implemented for CoreSimulator environments only;
90
+ # for Xcode < 6.0 this method does nothing.
91
+ #
92
+ # @param [Hash] launch_options These options need to contain the app bundle
93
+ # path and a udid that corresponds to a simulator name or simulator udid.
94
+ # In practical terms: call this after merging the original launch
95
+ # options with those options that are discovered.
96
+ #
97
+ # @param [RunLoop::SimControl] sim_control A simulator control object.
98
+ # @raise [RuntimeError] Raises an error if the `launch_options[:udid]`
99
+ # cannot be used to find a simulator.
100
+ # @raise [RunLoop::IncompatibleArchitecture] Raises an error if the
101
+ # application binary is not compatible with the target simulator.
102
+ def self.expect_compatible_simulator_architecture(launch_options, sim_control)
103
+ if sim_control.xcode_version_gte_6?
104
+ sim_identifier = launch_options[:udid]
105
+ simulator = sim_control.simulators.find do |simulator|
106
+ [simulator.instruments_identifier(sim_control.xctools),
107
+ simulator.udid].include?(sim_identifier)
108
+ end
109
+
110
+ if simulator.nil?
111
+ raise "Could not find simulator with identifier '#{sim_identifier}'"
112
+ end
113
+
114
+ lipo = RunLoop::Lipo.new(launch_options[:bundle_dir_or_bundle_id])
115
+ lipo.expect_compatible_arch(simulator)
116
+ if ENV['DEBUG'] == '1'
117
+ puts "Simulator instruction set '#{simulator.instruction_set}' is compatible with #{lipo.info}"
118
+ end
119
+ true
120
+ else
121
+ if ENV['DEBUG'] == '1'
122
+ puts "Xcode #{sim_control.xctools.xcode_version} detected; skipping simulator architecture check."
123
+ end
124
+ false
125
+ end
126
+ end
127
+
86
128
  def self.run_with_options(options)
87
129
  before = Time.now
88
130
 
@@ -91,11 +133,6 @@ module RunLoop
91
133
 
92
134
  RunLoop::Instruments.new.kill_instruments(xctools)
93
135
 
94
- if self.simulator_target?(options, sim_control)
95
- # @todo only enable accessibility on the targeted simulator
96
- sim_control.enable_accessibility_on_sims({:verbose => false})
97
- end
98
-
99
136
  device_target = options[:udid] || options[:device_target] || detect_connected_device || 'simulator'
100
137
  if device_target && device_target.to_s.downcase == 'device'
101
138
  device_target = detect_connected_device
@@ -150,7 +187,7 @@ module RunLoop
150
187
  log_file ||= File.join(results_dir, 'run_loop.out')
151
188
 
152
189
  after = Time.now
153
- if ENV['DEBUG']=='1'
190
+ if ENV['DEBUG'] == '1'
154
191
  puts "Preparation took #{after-before} seconds"
155
192
  end
156
193
 
@@ -166,6 +203,12 @@ module RunLoop
166
203
  }
167
204
  merged_options = options.merge(discovered_options)
168
205
 
206
+ if self.simulator_target?(merged_options, sim_control)
207
+ # @todo only enable accessibility on the targeted simulator
208
+ sim_control.enable_accessibility_on_sims({:verbose => false})
209
+ self.expect_compatible_simulator_architecture(merged_options, sim_control)
210
+ end
211
+
169
212
  self.log_run_loop_options(merged_options, xctools)
170
213
 
171
214
  cmd = instruments_command(merged_options, xctools)
@@ -819,9 +862,9 @@ module RunLoop
819
862
  script
820
863
  end
821
864
 
822
- def self.log_info(device_logger, message)
865
+ def self.log_info(logger, message)
823
866
  msg = "#{Time.now}: #{message}"
824
- if device_logger && device_logger.respond_to?(:info)
867
+ if logger && logger.respond_to?(:info)
825
868
  logger.info(msg)
826
869
  else
827
870
  puts msg if ENV['DEBUG'] == '1'
@@ -17,18 +17,6 @@ module RunLoop
17
17
  @udid = udid
18
18
  end
19
19
 
20
- # Is this device a simulator?
21
- # @return [Boolean] Return true if this device is a simulator.
22
- def simulator?
23
- not physical_device?
24
- end
25
-
26
- # Is this is a physical device?
27
- # @return [Boolean] Return true if this is a physical device.
28
- def physical_device?
29
- (self.udid =~ /[a-f0-9]{40}/) == 0
30
- end
31
-
32
20
  # Returns and instruments-ready device identifier that is a suitable value
33
21
  # for DEVICE_TARGET environment variable.
34
22
  #
@@ -50,5 +38,41 @@ module RunLoop
50
38
  "#{self.name} (#{version_part} Simulator)"
51
39
  end
52
40
  end
41
+
42
+ # Is this a physical device?
43
+ # @return [Boolean] Returns true if this is a device.
44
+ def physical_device?
45
+ not self.udid[/[a-f0-9]{40}/, 0].nil?
46
+ end
47
+
48
+ # Is this a simulator?
49
+ # @return [Boolean] Returns true if this is a simulator.
50
+ def simulator?
51
+ not self.physical_device?
52
+ end
53
+
54
+ # Return the instruction set for this device.
55
+ #
56
+ # **Simulator**
57
+ # The simulator instruction set will be i386 or x86_64 depending on the
58
+ # the (marketing) name of the device.
59
+ #
60
+ # @note Finding the instruction set of a device requires a third-party tool
61
+ # like ideviceinfo. Example:
62
+ # `$ ideviceinfo -u 89b59 < snip > ab7ba --key 'CPUArchitecture' => arm64`
63
+ #
64
+ # @raise [RuntimeError] Raises an error if this device is a physical device.
65
+ # @return [String] An instruction set.
66
+ def instruction_set
67
+ if self.simulator?
68
+ if ['iPhone 4s', 'iPhone 5', 'iPad 2', 'iPad Retina'].include?(self.name)
69
+ 'i386'
70
+ else
71
+ 'x86_64'
72
+ end
73
+ else
74
+ raise 'Finding the instruction set of a device requires a third-party tool like ideviceinfo'
75
+ end
76
+ end
53
77
  end
54
78
  end
@@ -0,0 +1,107 @@
1
+ require 'open3'
2
+
3
+ module RunLoop
4
+
5
+ # An error class for signaling an incompatible architecture.
6
+ class IncompatibleArchitecture < StandardError
7
+ end
8
+
9
+ # A class for interacting with the lipo command-line tool to verify that an
10
+ # executable is valid for the test target (device or simulator).
11
+ #
12
+ # @note All lipo commands are run in the context of `xcrun`.
13
+ class Lipo
14
+
15
+ # The path to the application bundle we are inspecting.
16
+ # @!attribute [wr] bundle_path
17
+ # @return [String] The path to the application bundle (.app).
18
+ attr_accessor :bundle_path
19
+
20
+ def initialize(bundle_path)
21
+ @bundle_path = bundle_path
22
+ @plist_buddy = RunLoop::PlistBuddy.new
23
+ end
24
+
25
+ # Inspect the `CFBundleExecutable` in the app bundle path with `lipo` and
26
+ # compare the result with the target device's instruction set.
27
+ #
28
+ # **Simulators**
29
+ #
30
+ # If the target is a simulator and the binary contains an i386 slice, the
31
+ # app will launch on the 64-bit simulators.
32
+ #
33
+ # If the target is a simulator and the binary contains _only_ an x86_64
34
+ # slice, the app will not launch on these simulators:
35
+ #
36
+ # ```
37
+ # iPhone 4S, iPad 2, iPhone 5, and iPad Retina.
38
+ # ```
39
+ #
40
+ # All other simulators are 64-bit.
41
+ #
42
+ # **Devices**
43
+ #
44
+ # @see {https://www.innerfence.com/howto/apple-ios-devices-dates-versions-instruction-sets}
45
+ #
46
+ # ```
47
+ # armv7 <== 3gs, 4s, iPad 2, iPad mini, iPad 3, iPod 3, iPod 4, iPod 5
48
+ # armv7s <== 5, 5c, iPad 4
49
+ # arm64 <== 5s, 6, 6 Plus, Air, Air 2, iPad Mini Retina, iPad Mini 3
50
+ # ```
51
+ #
52
+ # @note At the moment, we are focusing on simulator compatibility. Since we
53
+ # don't have an automated way of installing an .ipa on local device, we
54
+ # don't require an .ipa path. Without an .ipa path, we cannot verify the
55
+ # architectures. Further, we would need to adopt a third-party tool like
56
+ # ideviceinfo to find the target device's instruction set.
57
+ # @param [RunLoop::Device] device The test target.
58
+ # @raise [RuntimeError] Raises an error if the device is a physical device.
59
+ # @raise [RunLoop::IncompatibleArchitecture] Raises an error if the instruction set of the target
60
+ # device is not compatible with the executable in the application.
61
+ def expect_compatible_arch(device)
62
+ if device.physical_device?
63
+ raise 'Ensuring compatible arches for physical devices is NYI'
64
+ else
65
+ arches = self.info
66
+ # An i386 binary will run on any simulator.
67
+ return true if arches.include?('i386')
68
+
69
+ instruction_set = device.instruction_set
70
+ unless arches.include?(instruction_set)
71
+ raise RunLoop::IncompatibleArchitecture,
72
+ ['Binary at:',
73
+ binary_path,
74
+ "does not contain a compatible architecture for target device.",
75
+ "Expected '#{instruction_set}' but found #{arches}."].join("\n")
76
+ end
77
+ end
78
+ end
79
+
80
+ # Returns a list of architecture in the binary.
81
+ # @return [Array<String>] A list of architecture.
82
+ def info
83
+ execute_lipo("-info #{binary_path}") do |stdout, _, _|
84
+ output = stdout.read.strip
85
+ output.split(':')[-1].strip.split
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def execute_lipo(argument)
92
+ command = "xcrun lipo #{argument}"
93
+ Open3.popen3(command) do |_, stdout, stderr, wait_thr|
94
+ yield stdout, stderr, wait_thr
95
+ end
96
+ end
97
+
98
+ def plist_path
99
+ File.join(@bundle_path, 'Info.plist');
100
+ end
101
+
102
+ def binary_path
103
+ binary_relative_path = @plist_buddy.plist_read('CFBundleExecutable', plist_path)
104
+ File.join(@bundle_path, binary_relative_path)
105
+ end
106
+ end
107
+ end
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = '1.1.1.pre7'
2
+ VERSION = '1.1.1.pre8'
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
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: 1.1.1.pre7
4
+ version: 1.1.1.pre8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Krukow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-28 00:00:00.000000000 Z
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -222,6 +222,7 @@ files:
222
222
  - lib/run_loop/core.rb
223
223
  - lib/run_loop/device.rb
224
224
  - lib/run_loop/instruments.rb
225
+ - lib/run_loop/lipo.rb
225
226
  - lib/run_loop/plist_buddy.rb
226
227
  - lib/run_loop/sim_control.rb
227
228
  - lib/run_loop/version.rb