luffa 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +39 -0
- data/LICENSE +204 -0
- data/README.md +7 -0
- data/VERSIONING.md +16 -0
- data/bin/luffa +17 -0
- data/lib/luffa.rb +12 -0
- data/lib/luffa/cli/cli.rb +65 -0
- data/lib/luffa/environment.rb +31 -0
- data/lib/luffa/gem.rb +35 -0
- data/lib/luffa/ios/ideviceinstaller.rb +116 -0
- data/lib/luffa/ios/simulator.rb +36 -0
- data/lib/luffa/ios/xcode.rb +93 -0
- data/lib/luffa/logging.rb +21 -0
- data/lib/luffa/patches/awesome_print.rb +16 -0
- data/lib/luffa/patches/kernel.rb +19 -0
- data/lib/luffa/retry.rb +13 -0
- data/lib/luffa/unix_command.rb +43 -0
- data/lib/luffa/version.rb +166 -0
- data/lib/luffa/with_debugging.rb +13 -0
- data/script/ci/com.apple.dt.instruments.process.analysis.plist +34 -0
- data/script/ci/com.apple.dt.instruments.process.kill.plist +34 -0
- metadata +323 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Luffa
|
4
|
+
class Simulator
|
5
|
+
def core_simulator_home_dir
|
6
|
+
@core_simulator_home_dir ||= File.expand_path('~/Library/Developer/CoreSimulator')
|
7
|
+
end
|
8
|
+
|
9
|
+
def core_simulator_device_dir(sim_udid=nil)
|
10
|
+
if sim_udid.nil?
|
11
|
+
@core_simulator_device_dir ||= File.expand_path(File.join(core_simulator_home_dir, 'Devices'))
|
12
|
+
else
|
13
|
+
File.expand_path(File.join(core_simulator_device_dir, sim_udid))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def core_simulator_device_containers_dir(sim_udid)
|
18
|
+
File.expand_path(File.join(core_simulator_device_dir(sim_udid), 'Containers'))
|
19
|
+
end
|
20
|
+
|
21
|
+
def core_simulator_for_xcode_version(idiom, form_factor, xcode_version)
|
22
|
+
if xcode_version < Luffa::Version.new('6.1')
|
23
|
+
ios_version = '8.0'
|
24
|
+
elsif xcode_version < Luffa::Version.new('6.2')
|
25
|
+
ios_version = '8.1'
|
26
|
+
elsif xcode_version < Luffa::Version.new('6.3')
|
27
|
+
ios_version = '8.2'
|
28
|
+
elsif xcode_version >= Luffa::Version.new('6.3')
|
29
|
+
ios_version = '8.3'
|
30
|
+
else
|
31
|
+
raise "Unsupported Xcode version: #{xcode_version}"
|
32
|
+
end
|
33
|
+
"#{idiom} #{form_factor} (#{ios_version} Simulator)"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Luffa
|
2
|
+
class XcodeInstall
|
3
|
+
attr :version
|
4
|
+
attr :path
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
Luffa::Xcode.with_developer_dir(path) do
|
8
|
+
@version = lambda {
|
9
|
+
xcode_build_output = `xcrun xcodebuild -version`.split(/\s/)[1]
|
10
|
+
Luffa::Version.new(xcode_build_output)
|
11
|
+
}.call
|
12
|
+
|
13
|
+
@path = path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def version_string
|
18
|
+
version.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def == (other)
|
22
|
+
other.path == path && other.version == version
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Xcode
|
27
|
+
|
28
|
+
def self.with_developer_dir(developer_dir, &block)
|
29
|
+
original_developer_dir = Luffa::Environment.developer_dir
|
30
|
+
stripped = developer_dir.strip
|
31
|
+
begin
|
32
|
+
ENV['DEVELOPER_DIR'] = stripped
|
33
|
+
block.call
|
34
|
+
ensure
|
35
|
+
ENV['DEVELOPER_DIR'] = original_developer_dir
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.with_xcode_install(xcode_install, &block)
|
40
|
+
self.with_developer_dir(xcode_install.path, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.ios_version_incompatible_with_xcode_version?(ios_version, xcode_version)
|
44
|
+
[(ios_version >= Luffa::Version.new('8.0') && xcode_version < Luffa::Version.new('6.0')),
|
45
|
+
(ios_version >= Luffa::Version.new('8.1') && xcode_version < Luffa::Version.new('6.1')),
|
46
|
+
(ios_version >= Luffa::Version.new('8.2') && xcode_version < Luffa::Version.new('6.2')),
|
47
|
+
(ios_version >= Luffa::Version.new('8.3') && xcode_version < Luffa::Version.new('6.3'))].any?
|
48
|
+
end
|
49
|
+
|
50
|
+
def xcode_installs
|
51
|
+
@xcode_installs ||= lambda do
|
52
|
+
min_xcode_version = Luffa::Version.new('5.1.1')
|
53
|
+
active_xcode = Luffa::Xcode.new.active_xcode
|
54
|
+
xcodes = [active_xcode]
|
55
|
+
Dir.glob('/Xcode/*/*.app/Contents/Developer').each do |path|
|
56
|
+
xcode_version = path[/(\d\.\d(\.\d)?)/, 0]
|
57
|
+
if Luffa::Version.new(xcode_version) >= min_xcode_version
|
58
|
+
install = Luffa::XcodeInstall.new(path)
|
59
|
+
unless install == active_xcode
|
60
|
+
xcodes << install
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
xcodes
|
65
|
+
end.call
|
66
|
+
end
|
67
|
+
|
68
|
+
# The developer dir at the time the tests start.
|
69
|
+
#
|
70
|
+
# To change the active Xcode:
|
71
|
+
#
|
72
|
+
# $ DEVELOPER_DIR=/path/to/Xcode.app/Contents/Developer be rake spec
|
73
|
+
def active_xcode_dir
|
74
|
+
@active_xcode_dir ||= lambda {
|
75
|
+
env = Luffa::Environment.developer_dir
|
76
|
+
if env
|
77
|
+
env
|
78
|
+
else
|
79
|
+
# fall back to xcode-select
|
80
|
+
xcode_select_path
|
81
|
+
end
|
82
|
+
}.call
|
83
|
+
end
|
84
|
+
|
85
|
+
def xcode_select_path
|
86
|
+
@xcode_select_dir ||= `xcode-select --print-path`.strip
|
87
|
+
end
|
88
|
+
|
89
|
+
def active_xcode
|
90
|
+
@active_xcode ||= Luffa::XcodeInstall.new(active_xcode_dir)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Luffa
|
2
|
+
def self.log_unix_cmd(msg)
|
3
|
+
puts "\033[36mEXEC: #{msg}\033[0m" if msg
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.log_pass(msg)
|
7
|
+
puts "\033[32mPASS: #{msg}\033[0m" if msg
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.log_warn(msg)
|
11
|
+
puts "\033[34mWARN: #{msg}\033[0m"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.log_fail(msg)
|
15
|
+
puts "\033[31mFAIL: #{msg}\033[0m" if msg
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.log_info(msg)
|
19
|
+
puts "\033[46mINFO: #{msg}\033[0m" if msg
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
# monkey patch for AwesomePrint + objects that implement '=='
|
4
|
+
module AwesomePrint
|
5
|
+
class Formatter
|
6
|
+
def awesome_self(object, type)
|
7
|
+
if @options[:raw] && object.instance_variables.any?
|
8
|
+
awesome_object(object)
|
9
|
+
elsif object.respond_to?(:to_hash)
|
10
|
+
awesome_hash(object.to_hash)
|
11
|
+
else
|
12
|
+
colorize(object.inspect.to_s, type)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kernel
|
2
|
+
def capture_stdout
|
3
|
+
out = StringIO.new
|
4
|
+
$stdout = out
|
5
|
+
yield
|
6
|
+
return out
|
7
|
+
ensure
|
8
|
+
$stdout = STDOUT
|
9
|
+
end
|
10
|
+
|
11
|
+
def capture_stderr
|
12
|
+
out = StringIO.new
|
13
|
+
$stderr = out
|
14
|
+
yield
|
15
|
+
return out
|
16
|
+
ensure
|
17
|
+
$stderr = STDERR
|
18
|
+
end
|
19
|
+
end
|
data/lib/luffa/retry.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Luffa
|
2
|
+
def self.unix_command(cmd, opts={})
|
3
|
+
default_opts = {:pass_msg => nil,
|
4
|
+
:fail_msg => nil,
|
5
|
+
:exit_on_nonzero_status => true,
|
6
|
+
:env_vars => {},
|
7
|
+
:log_cmd => true,
|
8
|
+
:obscure_fields => []}
|
9
|
+
merged_opts = default_opts.merge(opts)
|
10
|
+
|
11
|
+
obscure_fields = merged_opts[:obscure_fields]
|
12
|
+
|
13
|
+
if not obscure_fields.empty? and merged_opts[:log_cmd]
|
14
|
+
obscured = cmd.split(' ').map do |token|
|
15
|
+
if obscure_fields.include? token
|
16
|
+
"#{token[0,1]}***#{token[token.length-1,1]}"
|
17
|
+
else
|
18
|
+
token
|
19
|
+
end
|
20
|
+
end
|
21
|
+
Luffa.log_unix_cmd obscured.join(' ')
|
22
|
+
elsif merged_opts[:log_cmd]
|
23
|
+
Luffa.log_unix_cmd cmd
|
24
|
+
end
|
25
|
+
|
26
|
+
exit_on_err = merged_opts[:exit_on_nonzero_status]
|
27
|
+
unless exit_on_err
|
28
|
+
system 'set +e'
|
29
|
+
end
|
30
|
+
|
31
|
+
env_vars = merged_opts[:env_vars]
|
32
|
+
res = system(env_vars, cmd)
|
33
|
+
exit_code = $?.exitstatus
|
34
|
+
if res
|
35
|
+
Luffa.log_pass merged_opts[:pass_msg]
|
36
|
+
else
|
37
|
+
Luffa.log_fail merged_opts[:fail_msg]
|
38
|
+
exit exit_code if exit_on_err
|
39
|
+
end
|
40
|
+
system 'set -e'
|
41
|
+
exit_code
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module Luffa
|
2
|
+
VERSION = '1.0.0'
|
3
|
+
|
4
|
+
# A model of a software release version that can be used to compare two versions.
|
5
|
+
#
|
6
|
+
# Calabash and RunLoop try very hard to comply with Semantic Versioning rules.
|
7
|
+
# However, the semantic versioning spec is incompatible with RubyGem's patterns
|
8
|
+
# for pre-release gems.
|
9
|
+
#
|
10
|
+
# > "But returning to the practical: No release version of SemVer is compatible with Rubygems." - _David Kellum_
|
11
|
+
#
|
12
|
+
# Calabash and RunLoop version numbers will be in the form `<major>.<minor>.<patch>[.pre<N>]`.
|
13
|
+
#
|
14
|
+
# @see http://semver.org/
|
15
|
+
# @see http://gravitext.com/2012/07/22/versioning.html
|
16
|
+
class Version
|
17
|
+
|
18
|
+
# @!attribute [rw] major
|
19
|
+
# @return [Integer] the major version
|
20
|
+
attr_accessor :major
|
21
|
+
|
22
|
+
# @!attribute [rw] minor
|
23
|
+
# @return [Integer] the minor version
|
24
|
+
attr_accessor :minor
|
25
|
+
|
26
|
+
# @!attribute [rw] patch
|
27
|
+
# @return [Integer] the patch version
|
28
|
+
attr_accessor :patch
|
29
|
+
|
30
|
+
# @!attribute [rw] pre
|
31
|
+
# @return [Boolean] true if this is a pre-release version
|
32
|
+
attr_accessor :pre
|
33
|
+
|
34
|
+
# @!attribute [rw] pre_version
|
35
|
+
# @return [Integer] if this is a pre-release version, returns the
|
36
|
+
# pre-release version; otherwise this is nil
|
37
|
+
attr_accessor :pre_version
|
38
|
+
|
39
|
+
# Creates a new Version instance with all the attributes set.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# version = Version.new(0.10.1)
|
43
|
+
# version.major => 0
|
44
|
+
# version.minor => 10
|
45
|
+
# version.patch => 1
|
46
|
+
# version.pre => false
|
47
|
+
# version.pre_release => nil
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# version = Version.new(1.6.3.pre5)
|
51
|
+
# version.major => 1
|
52
|
+
# version.minor => 6
|
53
|
+
# version.patch => 3
|
54
|
+
# version.pre => true
|
55
|
+
# version.pre_release => 5
|
56
|
+
#
|
57
|
+
# @param [String] version the version string to parse.
|
58
|
+
# @raise [ArgumentError] if version is not in the form 5, 6.1, 7.1.2, 8.2.3.pre1
|
59
|
+
def initialize(version)
|
60
|
+
tokens = version.strip.split('.')
|
61
|
+
count = tokens.count
|
62
|
+
if tokens.empty?
|
63
|
+
raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1"
|
64
|
+
end
|
65
|
+
|
66
|
+
if count < 4 and tokens.any? { |elm| elm =~ /\D/ }
|
67
|
+
raise ArgumentError, "expected '#{version}' to be like 5, 6.1, 7.1.2, 8.2.3.pre1"
|
68
|
+
end
|
69
|
+
|
70
|
+
if count == 4
|
71
|
+
@pre = tokens[3]
|
72
|
+
pre_tokens = @pre.scan(/\D+|\d+/)
|
73
|
+
@pre_version = pre_tokens[1].to_i if pre_tokens.count == 2
|
74
|
+
end
|
75
|
+
|
76
|
+
@major, @minor, @patch = version.split('.').map(&:to_i)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an string representation of this version.
|
80
|
+
# @return [String] a string in the form `<major>.<minor>.<patch>[.pre<N>]`
|
81
|
+
def to_s
|
82
|
+
str = [major, minor, patch].compact.join('.')
|
83
|
+
str = "#{str}.#{pre}" if pre
|
84
|
+
str
|
85
|
+
end
|
86
|
+
|
87
|
+
# Compare this version to another for equality.
|
88
|
+
# @param [Version] other the version to compare against
|
89
|
+
# @return [Boolean] true if this Version is the same as `other`
|
90
|
+
def == (other)
|
91
|
+
Version.compare(self, other) == 0
|
92
|
+
end
|
93
|
+
|
94
|
+
# Compare this version to another for inequality.
|
95
|
+
# @param [Version] other the version to compare against
|
96
|
+
# @return [Boolean] true if this Version is not the same as `other`
|
97
|
+
def != (other)
|
98
|
+
Version.compare(self, other) != 0
|
99
|
+
end
|
100
|
+
|
101
|
+
# Is this version less-than another version?
|
102
|
+
# @param [Version] other the version to compare against
|
103
|
+
# @return [Boolean] true if this Version is less-than `other`
|
104
|
+
def < (other)
|
105
|
+
Version.compare(self, other) < 0
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is this version greater-than another version?
|
109
|
+
# @param [Version] other the version to compare against
|
110
|
+
# @return [Boolean] true if this Version is greater-than `other`
|
111
|
+
def > (other)
|
112
|
+
Version.compare(self, other) > 0
|
113
|
+
end
|
114
|
+
|
115
|
+
# Is this version less-than or equal to another version?
|
116
|
+
# @param [Version] other the version to compare against
|
117
|
+
# @return [Boolean] true if this Version is less-than or equal `other`
|
118
|
+
def <= (other)
|
119
|
+
Version.compare(self, other) <= 0
|
120
|
+
end
|
121
|
+
|
122
|
+
# Is this version greater-than or equal to another version?
|
123
|
+
# @param [Version] other the version to compare against
|
124
|
+
# @return [Boolean] true if this Version is greater-than or equal `other`
|
125
|
+
def >= (other)
|
126
|
+
Version.compare(self, other) >= 0
|
127
|
+
end
|
128
|
+
|
129
|
+
# Compare version `a` to version `b`.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# compare Version.new(0.10.0), Version.new(0.9.0) => 1
|
133
|
+
# compare Version.new(0.9.0), Version.new(0.10.0) => -1
|
134
|
+
# compare Version.new(0.9.0), Version.new(0.9.0) => 0
|
135
|
+
#
|
136
|
+
# @return [Integer] an integer `(-1, 1)`
|
137
|
+
def self.compare(a, b)
|
138
|
+
|
139
|
+
if a.major != b.major
|
140
|
+
return a.major > b.major ? 1 : -1
|
141
|
+
end
|
142
|
+
|
143
|
+
if a.minor != b.minor
|
144
|
+
return a.minor.to_i > b.minor.to_i ? 1 : -1
|
145
|
+
end
|
146
|
+
|
147
|
+
if a.patch != b.patch
|
148
|
+
return a.patch.to_i > b.patch.to_i ? 1 : -1
|
149
|
+
end
|
150
|
+
|
151
|
+
return -1 if a.pre and (not a.pre_version) and b.pre_version
|
152
|
+
return 1 if a.pre_version and b.pre and (not b.pre_version)
|
153
|
+
|
154
|
+
return -1 if a.pre and (not b.pre)
|
155
|
+
return 1 if (not a.pre) and b.pre
|
156
|
+
|
157
|
+
return -1 if a.pre_version and (not b.pre_version)
|
158
|
+
return 1 if (not a.pre_version) and b.pre_version
|
159
|
+
|
160
|
+
if a.pre_version != b.pre_version
|
161
|
+
return a.pre_version.to_i > b.pre_version.to_i ? 1 : -1
|
162
|
+
end
|
163
|
+
0
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>allow-root</key>
|
6
|
+
<true/>
|
7
|
+
<key>authenticate-user</key>
|
8
|
+
<true/>
|
9
|
+
<key>class</key>
|
10
|
+
<string>user</string>
|
11
|
+
<key>comment</key>
|
12
|
+
<string>Rights for Instruments</string>
|
13
|
+
<key>created</key>
|
14
|
+
<real>409022991.255041</real>
|
15
|
+
<key>group</key>
|
16
|
+
<string>admin</string>
|
17
|
+
<key>identifier</key>
|
18
|
+
<string>com.apple.dt.instruments.dtsecurity.xpc</string>
|
19
|
+
<key>modified</key>
|
20
|
+
<real>409022991.255041</real>
|
21
|
+
<key>requirement</key>
|
22
|
+
<string>identifier "com.apple.dt.instruments.dtsecurity.xpc" and anchor apple</string>
|
23
|
+
<key>session-owner</key>
|
24
|
+
<false/>
|
25
|
+
<key>shared</key>
|
26
|
+
<true/>
|
27
|
+
<key>timeout</key>
|
28
|
+
<integer>36000</integer>
|
29
|
+
<key>tries</key>
|
30
|
+
<integer>10000</integer>
|
31
|
+
<key>version</key>
|
32
|
+
<integer>0</integer>
|
33
|
+
</dict>
|
34
|
+
</plist>
|