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 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
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg
3
+ .idea/
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cocoapods-unit-test.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'cocoapods'
8
+
9
+ gem 'mocha'
10
+ gem 'bacon'
11
+ gem 'mocha-on-bacon'
12
+ gem 'prettybacon'
13
+ end
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,13 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ def specs(dir)
4
+ FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ')
5
+ end
6
+
7
+ desc 'Runs all the specs'
8
+ task :specs do
9
+ sh "bundle exec bacon #{specs('**')}"
10
+ end
11
+
12
+ task :default => :specs
13
+
@@ -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,3 @@
1
+ module CocoapodsUnitTest
2
+ VERSION = "0.1.0"
3
+ 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
+
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ module Pod
4
+ describe Command::Test do
5
+ describe 'CLAide' do
6
+ it 'registers it self' do
7
+ Command.parse(%w{ test }).should.be.instance_of Command::Test
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -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