run_loop 1.1.1.pre7 → 1.1.1.pre8

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 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