cocoapods-unit-test 0.1.0
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 +7 -0
- data/.gitignore +3 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +13 -0
- data/cocoapods-unit-test.gemspec +29 -0
- data/lib/cocoapods-unit-test/command/test.rb +48 -0
- data/lib/cocoapods-unit-test/command.rb +1 -0
- data/lib/cocoapods-unit-test/executable.rb +146 -0
- data/lib/cocoapods-unit-test/gem_version.rb +3 -0
- data/lib/cocoapods-unit-test/project.rb +156 -0
- data/lib/cocoapods-unit-test/result.rb +56 -0
- data/lib/cocoapods-unit-test/xcodeproj/build_configuration.rb +24 -0
- data/lib/cocoapods-unit-test/xcodeproj/native_target.rb +19 -0
- data/lib/cocoapods-unit-test/xcodeproj/project.rb +23 -0
- data/lib/cocoapods-unit-test/xcodeproj/test_action.rb +43 -0
- data/lib/cocoapods-unit-test.rb +1 -0
- data/lib/cocoapods_plugin.rb +94 -0
- data/spec/command/test_spec.rb +12 -0
- data/spec/spec_helper.rb +50 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 141801ab890eb855285c6536ea74d179ea11bdd09de628f0107492b44af96065
|
4
|
+
data.tar.gz: 45213aa79c396edbd79749e4f759a65b092bcb651b123c2c1f617c20d002bf32
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87b5c7bc63cd7ca0d6ae7d84d3fef4ff175db3cbf2ac8e0b55605bb3d8006bf236eb5da62079141e93f516e6f4b40f7b3360eb6a89576de92bf82f4778d5098b
|
7
|
+
data.tar.gz: 7d869825eab17f3b1d923b335db37c7d7660d89ab34f11d202addd46cc3bfc14c7001a23e470c71cee48c677af0e5bddbee70aa57ed9fb68cab021acaa77d27f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2020 bob <bob170731@gmail.com>
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# cocoapods-unit-test
|
2
|
+
|
3
|
+
A cocoapods plugin to work with Unit-Test
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Just install it
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install cocoapods-unit-test
|
11
|
+
```
|
12
|
+
|
13
|
+
or use Gemfile with `bundle install`
|
14
|
+
|
15
|
+
```
|
16
|
+
source 'https://rubygems.org/'
|
17
|
+
|
18
|
+
gem 'cocoapods', '>= 1.8.4'
|
19
|
+
gem 'xcpretty','~> 0.3.0'
|
20
|
+
gem 'cocoapods-unit-test','~ 1.0'
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### 1. modify Podspec
|
26
|
+
|
27
|
+
- add test_spec
|
28
|
+
- add dependency XcodeCoverage
|
29
|
+
|
30
|
+
```
|
31
|
+
Pod::Spec.new do |s|
|
32
|
+
s.name = 'TestExample'
|
33
|
+
...
|
34
|
+
|
35
|
+
s.subspec 'Core' do |c|
|
36
|
+
...
|
37
|
+
end
|
38
|
+
|
39
|
+
s.test_spec 'Tests' do |h|
|
40
|
+
h.source_files = 'TestExample/Tests/**/*.{h,m}'
|
41
|
+
h.dependency 'TestExample/Core'
|
42
|
+
h.dependency 'XcodeCoverage','>= 1.3.2'
|
43
|
+
h.frameworks = 'UIKit','Foundation'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
```
|
48
|
+
|
49
|
+
### 2. Add plugin in Podfile
|
50
|
+
|
51
|
+
- if you just test with develop pod, ignore the names parameter
|
52
|
+
- or use names if you have more than one pod to Test
|
53
|
+
|
54
|
+
```
|
55
|
+
|
56
|
+
## use names if you have more than one pod to Test
|
57
|
+
plugin 'cocoapods-unit-test',
|
58
|
+
:names => ["TestExample"]
|
59
|
+
|
60
|
+
|
61
|
+
target 'Example' do
|
62
|
+
pod 'XcodeCoverage', '>= 1.3.2'
|
63
|
+
pod 'TestExample',
|
64
|
+
:path => '../',
|
65
|
+
:testspecs => ["Tests"]
|
66
|
+
end
|
67
|
+
|
68
|
+
```
|
69
|
+
|
70
|
+
### 3. run pod install
|
71
|
+
|
72
|
+
```
|
73
|
+
bundle exec pod install
|
74
|
+
```
|
75
|
+
|
76
|
+
### 4. Testing
|
77
|
+
|
78
|
+
- testing with cli with pod name `TestExample`
|
79
|
+
- simulator default is iPhone 8, you can change it
|
80
|
+
|
81
|
+
```
|
82
|
+
bundle exec pod test TestExample --simulator='iPhone 8'
|
83
|
+
```
|
84
|
+
|
85
|
+
- auto open result or you can check it
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
## Example
|
90
|
+
|
91
|
+
see []
|
92
|
+
|
93
|
+
## Contribute
|
94
|
+
|
95
|
+
if you like
|
96
|
+
|
97
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cocoapods-unit-test/gem_version.rb'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'cocoapods-unit-test'
|
8
|
+
spec.version = CocoapodsUnitTest::VERSION
|
9
|
+
spec.authors = ['bob']
|
10
|
+
spec.email = ['bob170731@gmail.com']
|
11
|
+
spec.description = %q{A short description of cocoapods-unit-test.}
|
12
|
+
spec.summary = %q{A longer description of cocoapods-unit-test.}
|
13
|
+
spec.homepage = 'https://github.com/DanboDuan/cocoapods-unit-test'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'xcodeproj','~> 1.10'
|
22
|
+
spec.add_dependency 'cocoapods-core','~> 1.8'
|
23
|
+
spec.add_dependency 'cocoapods','~> 1.8'
|
24
|
+
spec.add_dependency 'xcpretty','~> 0.3'
|
25
|
+
spec.add_dependency 'neatjson','~> 0.9'
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
28
|
+
spec.add_development_dependency 'rake','~> 10'
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'cocoapods-unit-test/project'
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
class Command
|
5
|
+
class Test < Command
|
6
|
+
|
7
|
+
self.summary = 'Short description of cocoapods-unit-test.'
|
8
|
+
self.command = 'test'
|
9
|
+
self.description = <<-DESC
|
10
|
+
Longer description of cocoapods-unit-test.
|
11
|
+
DESC
|
12
|
+
|
13
|
+
self.arguments = [
|
14
|
+
CLAide::Argument.new('NAME', true),
|
15
|
+
]
|
16
|
+
def self.options
|
17
|
+
options = [
|
18
|
+
['--reinstall', 're pod install'],
|
19
|
+
["--simulator='SIMULATOR'", "simulator name like 'iPhone 8'"],
|
20
|
+
]
|
21
|
+
options.concat(super.reject { |option, _| option == '--silent' })
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(argv)
|
25
|
+
@simulator = argv.option('simulator', 'iPhone 8')
|
26
|
+
@reinstall = argv.flag?('reinstall')
|
27
|
+
@name = argv.shift_argument
|
28
|
+
@project = TestProject.new(@name, @simulator)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate!
|
33
|
+
super
|
34
|
+
help! 'A Pod name is required.' unless @name
|
35
|
+
@project.validate!
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
UI.title "Runing Test..." do
|
40
|
+
@project.run(@reinstall)
|
41
|
+
end
|
42
|
+
|
43
|
+
UI.title "Finish Test..."
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'cocoapods-unit-test/command/test'
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'open3'
|
2
|
+
module Pod
|
3
|
+
module UnitExecutable
|
4
|
+
|
5
|
+
def executable(name)
|
6
|
+
unless respond_to?("#{name.to_s}!")
|
7
|
+
define_method(name.to_s + '!') do |*command|
|
8
|
+
UnitExecutable.execute_command(name, Array(command).flatten, true)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.execute_command(executable, command, raise_on_failure = true)
|
15
|
+
bin = which!(executable)
|
16
|
+
|
17
|
+
command = command.map(&:to_s)
|
18
|
+
if File.basename(bin) == 'tar.exe'
|
19
|
+
# Tar on Windows needs --force-local
|
20
|
+
command.push('--force-local')
|
21
|
+
end
|
22
|
+
full_command = "#{bin} #{command.join(' ')}"
|
23
|
+
|
24
|
+
stdout = Indenter.new(STDOUT)
|
25
|
+
stderr = Indenter.new(STDERR)
|
26
|
+
|
27
|
+
status = popen3(bin, command, stdout, stderr)
|
28
|
+
stdout = stdout.join
|
29
|
+
stderr = stderr.join
|
30
|
+
output = stdout + stderr
|
31
|
+
unless status.success?
|
32
|
+
UI.puts("$ #{full_command}")
|
33
|
+
if raise_on_failure
|
34
|
+
raise Informative, "#{full_command}\n\n#{output}"
|
35
|
+
else
|
36
|
+
UI.puts("[!] Failed: #{full_command}".red)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
output
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.which(program)
|
44
|
+
program = program.to_s
|
45
|
+
paths = ENV.fetch('PATH') { '' }.split(File::PATH_SEPARATOR)
|
46
|
+
paths.unshift('./')
|
47
|
+
paths.uniq!
|
48
|
+
paths.each do |path|
|
49
|
+
bin = File.expand_path(program, path)
|
50
|
+
if Gem.win_platform?
|
51
|
+
bin += '.exe'
|
52
|
+
end
|
53
|
+
if File.file?(bin) && File.executable?(bin)
|
54
|
+
return bin
|
55
|
+
end
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.which!(program)
|
61
|
+
which(program).tap do |bin|
|
62
|
+
raise Informative, "Unable to locate the executable `#{program}`" unless bin
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.capture_command(executable, command, capture: :merge, env: {}, **kwargs)
|
67
|
+
bin = which!(executable)
|
68
|
+
command = command.map(&:to_s)
|
69
|
+
case capture
|
70
|
+
when :merge then Open3.capture2e(env, [bin, bin], *command, **kwargs)
|
71
|
+
when :both then Open3.capture3(env, [bin, bin], *command, **kwargs)
|
72
|
+
when :out then Open3.capture3(env, [bin, bin], *command, **kwargs).values_at(0, -1)
|
73
|
+
when :err then Open3.capture3(env, [bin, bin], *command, **kwargs).drop(1)
|
74
|
+
when :none then Open3.capture3(env, [bin, bin], *command, **kwargs).last
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.capture_command!(executable, command, **kwargs)
|
79
|
+
capture_command(executable, command, **kwargs).tap do |result|
|
80
|
+
result = Array(result)
|
81
|
+
status = result.last
|
82
|
+
unless status.success?
|
83
|
+
output = result[0..-2].join
|
84
|
+
raise Informative, "#{bin} #{command.join(' ')}\n\n#{output}".strip
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def self.popen3(bin, command, stdout, stderr)
|
92
|
+
Open3.popen3(bin, *command) do |i, o, e, t|
|
93
|
+
reader(o, stdout)
|
94
|
+
reader(e, stderr)
|
95
|
+
i.close
|
96
|
+
|
97
|
+
status = t.value
|
98
|
+
|
99
|
+
o.flush
|
100
|
+
e.flush
|
101
|
+
sleep(0.01)
|
102
|
+
|
103
|
+
status
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.reader(input, output)
|
108
|
+
Thread.new do
|
109
|
+
buf = ''
|
110
|
+
begin
|
111
|
+
loop do
|
112
|
+
buf << input.readpartial(4096)
|
113
|
+
loop do
|
114
|
+
string, separator, buf = buf.partition(/[\r\n]/)
|
115
|
+
if separator.empty?
|
116
|
+
buf = string
|
117
|
+
break
|
118
|
+
end
|
119
|
+
output << (string << separator)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
rescue EOFError, IOError
|
123
|
+
output << (buf << $/) unless buf.empty?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
class Indenter < ::Array
|
130
|
+
attr_reader :indent
|
131
|
+
attr_reader :io
|
132
|
+
|
133
|
+
def initialize(io = nil)
|
134
|
+
@io = io
|
135
|
+
@indent = ' ' * UI.indentation_level
|
136
|
+
end
|
137
|
+
|
138
|
+
def <<(value)
|
139
|
+
super
|
140
|
+
io << "#{indent}#{value}" if io
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'cocoapods-unit-test/result'
|
4
|
+
require 'cocoapods-unit-test/xcodeproj/project'
|
5
|
+
|
6
|
+
module Pod
|
7
|
+
class TestProject
|
8
|
+
|
9
|
+
attr_accessor :path
|
10
|
+
attr_accessor :podfile
|
11
|
+
attr_accessor :pods_project
|
12
|
+
attr_accessor :workspace
|
13
|
+
attr_accessor :schemes
|
14
|
+
attr_accessor :target
|
15
|
+
attr_accessor :derived_data_path
|
16
|
+
attr_accessor :destination
|
17
|
+
attr_accessor :cov_shell
|
18
|
+
|
19
|
+
def initialize(name, simulator, path = Dir.pwd)
|
20
|
+
@destination = "platform=iOS Simulator,name=#{simulator}"
|
21
|
+
@target = name
|
22
|
+
@path = File.expand_path(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def podfile
|
26
|
+
@podfile ||= File.expand_path("Podfile", path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def workspace
|
30
|
+
@workspace ||= begin
|
31
|
+
ws = Dir["*.xcworkspace"].first
|
32
|
+
raise Informative, "No xcworkspace" if ws.nil? || ws.empty?
|
33
|
+
raise Informative, "No xcworkspace in #{path}" unless File.directory?(ws)
|
34
|
+
File.expand_path(ws, path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def schemes
|
39
|
+
@schemes ||= Dir[File.join(path, '**', 'xcschemes', '*.xcscheme')].select { |e|
|
40
|
+
e.include?("-Unit-")
|
41
|
+
}.map {|scheme|
|
42
|
+
File.basename(scheme, '.xcscheme')
|
43
|
+
}.select { |scheme|
|
44
|
+
scheme.start_with?("#{target}-Unit")
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def pods_project
|
49
|
+
@pods_project ||= begin
|
50
|
+
pods = Dir["**/*.xcodeproj"].select { |e| e.end_with?("Pods.xcodeproj") }
|
51
|
+
raise Informative, "can not find Pods.xcodeproj" if pods.empty?
|
52
|
+
pod = pods.first
|
53
|
+
raise Informative, "can not find Pods.xcodeproj in #{path}" unless File.directory?(pod)
|
54
|
+
File.expand_path(pod, path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_target!
|
59
|
+
project = Xcodeproj::Project.open(pods_project)
|
60
|
+
return true if project.unit_test_dev_pods().include?(@target)
|
61
|
+
return true if project.unit_test_dependency_pods().include?(@target)
|
62
|
+
raise Informative, "can not find test target #{@target} in Pods.xcodeproj"
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
def derived_data_path
|
67
|
+
@derived_data_path ||= File.expand_path("build/derivedDataPath", path)
|
68
|
+
end
|
69
|
+
|
70
|
+
def result_bundle_path(scheme)
|
71
|
+
File.expand_path("build/#{scheme}/Test.xcresult", path)
|
72
|
+
end
|
73
|
+
|
74
|
+
def result_json_path(scheme)
|
75
|
+
File.expand_path("build/#{scheme}/TestResult.json", path)
|
76
|
+
end
|
77
|
+
|
78
|
+
def destination
|
79
|
+
@destination ||= "platform=iOS Simulator,name=iPhone 8"
|
80
|
+
end
|
81
|
+
|
82
|
+
def archives_path(scheme)
|
83
|
+
File.expand_path("build/#{scheme}/archives", path)
|
84
|
+
end
|
85
|
+
|
86
|
+
def cov_shell
|
87
|
+
@cov_shell ||= File.expand_path("Pods/XcodeCoverage/getcov", path)
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate!
|
91
|
+
raise Informative, "No Podfile!" unless File.file?(podfile)
|
92
|
+
validate_target!
|
93
|
+
raise Informative, "No Test schemes!" if schemes.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
def pod_install
|
97
|
+
UI.puts "pod install..."
|
98
|
+
raise Informative, "Please use a Gemfile or run pod install by yourself" unless File.file?("Gemfile")
|
99
|
+
system( "bundle install")
|
100
|
+
system( "bundle exec pod install")
|
101
|
+
raise Informative, "XcodeCoverage not found" unless File.file?(cov_shell)
|
102
|
+
raise Informative, "workspace not found" unless File.directory?(workspace)
|
103
|
+
end
|
104
|
+
|
105
|
+
def run_test_with_scheme(scheme)
|
106
|
+
system("rm -fr #{result_bundle_path(scheme)}")
|
107
|
+
system("rm -fr #{result_json_path(scheme)}")
|
108
|
+
system("rm -fr #{archives_path(scheme)}")
|
109
|
+
system("mkdir -p #{archives_path(scheme)}")
|
110
|
+
|
111
|
+
cmd = %W(xcodebuild test -workspace #{workspace} -scheme #{scheme} -UseModernBuildSystem=NO
|
112
|
+
-derivedDataPath #{derived_data_path} -resultBundlePath #{result_bundle_path(scheme)}) + [
|
113
|
+
"-destination '#{destination}'",
|
114
|
+
"| xcpretty",
|
115
|
+
]
|
116
|
+
|
117
|
+
UI.puts cmd.join(" ")
|
118
|
+
system(cmd.join(" "))
|
119
|
+
|
120
|
+
cmd = %W(xcrun xcresulttool get --path #{result_bundle_path(scheme)} --format json) + [
|
121
|
+
"> #{result_json_path(scheme)}",
|
122
|
+
]
|
123
|
+
UI.puts cmd.join(" ")
|
124
|
+
system(cmd.join(" "))
|
125
|
+
|
126
|
+
cmd = %W(#{cov_shell} -x -o #{archives_path(scheme)})
|
127
|
+
UI.puts cmd.join(" ")
|
128
|
+
system(cmd.join(" "))
|
129
|
+
|
130
|
+
index_file = File.join(archives_path(scheme),"lcov/index.html")
|
131
|
+
TestResult.new(result_json_path(scheme),index_file).parse()
|
132
|
+
UI.puts "Test result path #{index_file}"
|
133
|
+
system("open #{index_file}") if File.file?(index_file)
|
134
|
+
end
|
135
|
+
|
136
|
+
def run_test
|
137
|
+
UI.title "Test target: #{target}" do
|
138
|
+
schemes.each { |e| UI.puts "Test Schemes: #{e}" }
|
139
|
+
end
|
140
|
+
|
141
|
+
system( "rm -fr #{derived_data_path}")
|
142
|
+
system( "mkdir -p #{derived_data_path}")
|
143
|
+
|
144
|
+
schemes.each do |scheme|
|
145
|
+
UI.puts "Testing #{scheme}..."
|
146
|
+
run_test_with_scheme(scheme)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def run(install_flag)
|
152
|
+
pod_install if install_flag
|
153
|
+
run_test
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
require 'rubygems'
|
3
|
+
require "json"
|
4
|
+
require 'neatjson'
|
5
|
+
|
6
|
+
module Pod
|
7
|
+
class TestResult
|
8
|
+
attr_accessor :cov_path
|
9
|
+
attr_accessor :json_path
|
10
|
+
|
11
|
+
def initialize(json_path, cov_path)
|
12
|
+
@json_path = File.expand_path(json_path)
|
13
|
+
@cov_path = File.expand_path(cov_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_json_result
|
17
|
+
## build_success, tests_count, tests_failed_count
|
18
|
+
load_json_from_file.fetch("metrics", {}).tap { |result|
|
19
|
+
UI.puts "Test case count :" + result.fetch("testsCount", {}).fetch("_value", 0).to_s
|
20
|
+
UI.puts "Test case fail count :" + result.fetch("testsFailedCount", {}).fetch("_value", 0).to_s
|
21
|
+
|
22
|
+
result.fetch("issues", {}).fetch("errorSummaries", {}).fetch("_values", []).each { |issues|
|
23
|
+
UI.puts "❌ #{issues.fetch("issueType", {}).fetch("_value", "").to_s} -> #{issues.fetch("message", {}).fetch("_value", "").to_s}"
|
24
|
+
}
|
25
|
+
|
26
|
+
result.fetch("issues", {}).fetch("testFailureSummaries", {}).fetch("_values", []).each { |issues|
|
27
|
+
UI.puts "❌ #{issues.fetch("testCaseName", {}).fetch("_value", "").to_s} -> #{issues.fetch("message", {}).fetch("_value", "").to_s}"
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_cov_result
|
33
|
+
## line_cov fun_cov headerCovTableEntry
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse
|
37
|
+
parse_json_result
|
38
|
+
parse_cov_result
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_json_from_file
|
42
|
+
if File.file?(json_path)
|
43
|
+
json_file = File.open json_path
|
44
|
+
return JSON.load json_file
|
45
|
+
end
|
46
|
+
{}
|
47
|
+
end
|
48
|
+
|
49
|
+
def dump_json_to_file(result, file)
|
50
|
+
File.open(file,"w") do |w|
|
51
|
+
w.write(JSON.neat_generate(result, sort:true, wrap:true, after_colon:1))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
module Object
|
6
|
+
# Encapsulates the information a specific build configuration referenced
|
7
|
+
# by a {XCConfigurationList} which in turn might be referenced by a
|
8
|
+
# {PBXProject} or a {PBXNativeTarget}.
|
9
|
+
#
|
10
|
+
class XCBuildConfiguration
|
11
|
+
|
12
|
+
def coverage_build_settings=(flag)
|
13
|
+
value = 'NO'
|
14
|
+
value = 'YES' if flag
|
15
|
+
|
16
|
+
build_settings['CLANG_ENABLE_CODE_COVERAGE'] = value
|
17
|
+
build_settings['GCC_INSTRUMENT_PROGRAM_FLOW_ARCS'] = value
|
18
|
+
build_settings['GCC_GENERATE_TEST_COVERAGE_FILES'] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
module Object
|
6
|
+
class AbstractTarget
|
7
|
+
|
8
|
+
def add_coverage_script_phase
|
9
|
+
if shell_script_build_phases.select { |e| e.name == "CodeCoverageScripts" }.length < 1
|
10
|
+
codeCoverageScript = new_shell_script_build_phase()
|
11
|
+
codeCoverageScript.name = "CodeCoverageScripts"
|
12
|
+
codeCoverageScript.shell_path = "/bin/bash"
|
13
|
+
codeCoverageScript.shell_script = "${SRCROOT}/XcodeCoverage/exportenv.sh"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
|
3
|
+
module Xcodeproj
|
4
|
+
class Project
|
5
|
+
|
6
|
+
def unit_test_dev_pods
|
7
|
+
return groups.select { |e| e.name == "Development Pods" }.map { |e|
|
8
|
+
e.children.map { |x| x.to_s }
|
9
|
+
}.flatten.uniq
|
10
|
+
end
|
11
|
+
|
12
|
+
def unit_test_dependency_pods
|
13
|
+
return groups.select { |e| e.name == "Pods" }.map { |e|
|
14
|
+
e.children.map { |x| x.to_s }
|
15
|
+
}.flatten.uniq
|
16
|
+
end
|
17
|
+
|
18
|
+
def unit_test_all_pods
|
19
|
+
unit_test_dev_pods() + unit_test_dependency_pods()
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'xcodeproj'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
module Xcodeproj
|
5
|
+
class XCScheme
|
6
|
+
# This class wraps the TestAction node of a .xcscheme XML file
|
7
|
+
#
|
8
|
+
class TestAction
|
9
|
+
|
10
|
+
def coverage_specified_targets?
|
11
|
+
string_to_bool(@xml_element.attributes['onlyGenerateCoverageForSpecifiedTargets'])
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Bool] flag
|
15
|
+
def coverage_specified_targets=(flag)
|
16
|
+
@xml_element.attributes['onlyGenerateCoverageForSpecifiedTargets'] = bool_to_string(flag)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param [BuildableReference] targets
|
20
|
+
#
|
21
|
+
def add_coverage_target_ref(ref)
|
22
|
+
coverage_targets = @xml_element.elements['CodeCoverageTargets'] || @xml_element.add_element('CodeCoverageTargets')
|
23
|
+
|
24
|
+
added = coverage_targets.get_elements('BuildableReference').map { |node|
|
25
|
+
BuildableReference.new(node)
|
26
|
+
}.select { |build| build.target_name == ref.target_name }
|
27
|
+
|
28
|
+
if added.empty?
|
29
|
+
coverage_targets.add_element(ref.xml_element)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [Xcodeproj::Project::Object::AbstractTarget] test_target
|
34
|
+
# @param [Xcodeproj::Project] the root project to reference from
|
35
|
+
# (when nil the project of the target is used)
|
36
|
+
def add_coverage_target(target, root_project = nil)
|
37
|
+
add_coverage_target_ref BuildableReference.new(target, root_project) unless target.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'cocoapods-unit-test/gem_version'
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'cocoapods-unit-test/command'
|
2
|
+
require 'cocoapods-unit-test/xcodeproj/test_action'
|
3
|
+
require 'cocoapods-unit-test/xcodeproj/native_target'
|
4
|
+
require 'cocoapods-unit-test/xcodeproj/build_configuration'
|
5
|
+
require 'cocoapods-unit-test/xcodeproj/project'
|
6
|
+
|
7
|
+
require 'cocoapods-core'
|
8
|
+
require 'xcodeproj'
|
9
|
+
require 'cocoapods'
|
10
|
+
|
11
|
+
module CocoapodsUnitTest
|
12
|
+
|
13
|
+
def self.modify_build_settings(target, is_target)
|
14
|
+
target.build_configurations.each {|config| config.coverage_build_settings = is_target}
|
15
|
+
if is_target
|
16
|
+
target.add_coverage_script_phase()
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.modify_schemes_settings(xcproj, test_specs)
|
21
|
+
project_path = xcproj.path
|
22
|
+
test_targets = xcproj.targets.select { |target| test_specs.include?(target.name.to_s) }
|
23
|
+
|
24
|
+
Dir[File.join(project_path, 'xcuserdata', '**', 'xcschemes', '*.xcscheme')].select { |e|
|
25
|
+
!test_targets.select { |target|
|
26
|
+
File.basename(e, '.xcscheme').start_with?("#{target.name.to_s}-Unit")
|
27
|
+
}.empty?
|
28
|
+
}.each do |path|
|
29
|
+
scheme = File.basename(path, '.xcscheme')
|
30
|
+
puts "Check scheme: #{scheme}"
|
31
|
+
scheme_targets = test_targets.select { |e| scheme.include?(e.name.to_s) }
|
32
|
+
unless scheme_targets.empty?
|
33
|
+
puts "Test scheme: #{scheme} with targets: " + scheme_targets.map { |e| e.name.to_s }.join(",")
|
34
|
+
xcproj_scheme = Xcodeproj::XCScheme.new(file_path = path)
|
35
|
+
testAction = xcproj_scheme.test_action
|
36
|
+
testAction.code_coverage_enabled = true
|
37
|
+
testAction.coverage_specified_targets = true
|
38
|
+
scheme_targets.each { |e| testAction.add_coverage_target(e) }
|
39
|
+
# xcproj_scheme.save!
|
40
|
+
File.delete(path)
|
41
|
+
xcproj_scheme.save_as(project_path, scheme)
|
42
|
+
else
|
43
|
+
File.delete(path)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Pod::HooksManager.register('cocoapods-unit-test', :post_install) do |context, user_options|
|
49
|
+
project_path = File.dirname(context.pods_project.path)
|
50
|
+
puts project_path
|
51
|
+
Dir[File.join(project_path,"**" ,'xcshareddata', '**', 'xcschemes', '*.xcscheme')].select { |e|
|
52
|
+
e.include?("-Unit-")
|
53
|
+
}.each do |path|
|
54
|
+
puts "Clean Tests xcscheme #{File.basename(path, '.xcscheme')}"
|
55
|
+
File.delete(path)
|
56
|
+
end
|
57
|
+
|
58
|
+
coverages = context.pods_project.unit_test_dependency_pods().select { |e| e == "XcodeCoverage" }
|
59
|
+
if coverages.empty?
|
60
|
+
puts "can not find XcodeCoverage"
|
61
|
+
next
|
62
|
+
else
|
63
|
+
puts "XcodeCoverage ready"
|
64
|
+
end
|
65
|
+
|
66
|
+
test_specs = user_options.fetch(:names,[])
|
67
|
+
|
68
|
+
if test_specs.nil? || test_specs.empty?
|
69
|
+
test_specs = context.pods_project.unit_test_dev_pods()
|
70
|
+
end
|
71
|
+
|
72
|
+
all_pods = context.pods_project.unit_test_all_pods()
|
73
|
+
|
74
|
+
test_specs = test_specs.select { |e| all_pods.include?(e) }
|
75
|
+
|
76
|
+
test_specs.each { |x| puts "Test target: #{x.to_s}"}
|
77
|
+
if test_specs.empty?
|
78
|
+
puts "can not find Test targets"
|
79
|
+
next
|
80
|
+
end
|
81
|
+
|
82
|
+
Dir["**/*.xcodeproj"].each do |path|
|
83
|
+
xcproj = Xcodeproj::Project.open(path)
|
84
|
+
xcproj.targets.each do |target|
|
85
|
+
CocoapodsUnitTest.modify_build_settings(target, test_specs.include?(target.name.to_s))
|
86
|
+
end
|
87
|
+
CocoapodsUnitTest.modify_schemes_settings(xcproj, test_specs)
|
88
|
+
xcproj.save
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
ROOT = Pathname.new(File.expand_path('../../', __FILE__))
|
3
|
+
$:.unshift((ROOT + 'lib').to_s)
|
4
|
+
$:.unshift((ROOT + 'spec').to_s)
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'bacon'
|
8
|
+
require 'mocha-on-bacon'
|
9
|
+
require 'pretty_bacon'
|
10
|
+
require 'pathname'
|
11
|
+
require 'cocoapods'
|
12
|
+
|
13
|
+
Mocha::Configuration.prevent(:stubbing_non_existent_method)
|
14
|
+
|
15
|
+
require 'cocoapods_plugin'
|
16
|
+
|
17
|
+
#-----------------------------------------------------------------------------#
|
18
|
+
|
19
|
+
module Pod
|
20
|
+
|
21
|
+
# Disable the wrapping so the output is deterministic in the tests.
|
22
|
+
#
|
23
|
+
UI.disable_wrap = true
|
24
|
+
|
25
|
+
# Redirects the messages to an internal store.
|
26
|
+
#
|
27
|
+
module UI
|
28
|
+
@output = ''
|
29
|
+
@warnings = ''
|
30
|
+
|
31
|
+
class << self
|
32
|
+
attr_accessor :output
|
33
|
+
attr_accessor :warnings
|
34
|
+
|
35
|
+
def puts(message = '')
|
36
|
+
@output << "#{message}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
def warn(message = '', actions = [])
|
40
|
+
@warnings << "#{message}\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
def print(message)
|
44
|
+
@output << message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#-----------------------------------------------------------------------------#
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cocoapods-unit-test
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- bob
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: xcodeproj
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cocoapods-core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: cocoapods
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: xcpretty
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.3'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: neatjson
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.9'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.9'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.3'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.3'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10'
|
111
|
+
description: A short description of cocoapods-unit-test.
|
112
|
+
email:
|
113
|
+
- bob170731@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- cocoapods-unit-test.gemspec
|
124
|
+
- lib/cocoapods-unit-test.rb
|
125
|
+
- lib/cocoapods-unit-test/command.rb
|
126
|
+
- lib/cocoapods-unit-test/command/test.rb
|
127
|
+
- lib/cocoapods-unit-test/executable.rb
|
128
|
+
- lib/cocoapods-unit-test/gem_version.rb
|
129
|
+
- lib/cocoapods-unit-test/project.rb
|
130
|
+
- lib/cocoapods-unit-test/result.rb
|
131
|
+
- lib/cocoapods-unit-test/xcodeproj/build_configuration.rb
|
132
|
+
- lib/cocoapods-unit-test/xcodeproj/native_target.rb
|
133
|
+
- lib/cocoapods-unit-test/xcodeproj/project.rb
|
134
|
+
- lib/cocoapods-unit-test/xcodeproj/test_action.rb
|
135
|
+
- lib/cocoapods_plugin.rb
|
136
|
+
- spec/command/test_spec.rb
|
137
|
+
- spec/spec_helper.rb
|
138
|
+
homepage: https://github.com/DanboDuan/cocoapods-unit-test
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata: {}
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubygems_version: 3.1.2
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: A longer description of cocoapods-unit-test.
|
161
|
+
test_files:
|
162
|
+
- spec/command/test_spec.rb
|
163
|
+
- spec/spec_helper.rb
|