trainer 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d3f3f7766506007d3f80b42de82fe1c92582ffe
4
+ data.tar.gz: c04661b81b99b5fe24e056609fce852280f63935
5
+ SHA512:
6
+ metadata.gz: 2fa78e21f1ff0585fd779781f5d3ffc76998637c326be9beb496f10311a5a8c7a35d51703db2efe57a4a0c13a9a9c3c0df5856662c1fb7c3730b6d1a227ad96a
7
+ data.tar.gz: c8765f4ebbe0de90fc45a07b484e6d45b39a82414aa9a60f49242f57f86dcec4667090d1ddc37bedd77f8c4faeb747ca0e709b3941bdadf2a5087a74bc716bc6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Felix Krause
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # trainer
2
+
3
+ This is an alternative approach to generate JUnit files for your CI (e.g. Jenkins) without parsing the `xcodebuild` output, but using the Xcode `plist` files instead.
4
+
5
+ The new Xcode beta has a known issue around not properly closing `stdout` ([Radar](https://openradar.appspot.com/27447948)), so you [can't use xcpretty](https://github.com/supermarin/xcpretty/issues/227).
6
+
7
+ `trainer` is a more robust approach on parsing test results based on the `plist` files that get generated by `xcodebuild`.
8
+
9
+ ## Use with [fastlane](https://fastlane.tools)
10
+
11
+ Update to the latest [fastlane](https://fastlane.tools) and run
12
+
13
+ ```bash
14
+ fastlane add_plugin trainer
15
+ ```
16
+
17
+ Now add the following to your `Fastfile`
18
+
19
+ ```ruby
20
+ lane :test do
21
+ scan(workspace: "MyApp.xcworkspace")
22
+ trainer
23
+ end
24
+ ```
25
+
26
+ For more information, check out the [fastlane plugin docs](fastlane-plugin-trainer#readme).
27
+
28
+ ## Without [fastlane](https://fastlane.tools)
29
+
30
+ ### Installation
31
+
32
+ Add this to your `Gemfile`
33
+ ```
34
+ gem trainer
35
+ ```
36
+ and run
37
+ ```
38
+ bundle install
39
+ ```
40
+
41
+ Alternatively you can install the gem system-wide using `sudo gem install trainer`.
42
+
43
+ ### Usage
44
+
45
+ If you use `fastlane`, check out the official [fastlane plugin](fastlane-plugin-trainer#readme) on how to use `trainer` in `fastlane`.
46
+
47
+ #### Run tests
48
+
49
+ ```
50
+ cd [project]
51
+ scan --derived_data_path "output"
52
+ ```
53
+
54
+ #### Convert the plist files to junit
55
+
56
+ ```
57
+ trainer
58
+ ```
59
+
60
+ You can also pass a custom directory containing the plist files
61
+
62
+ ```
63
+ trainer --path ./something
64
+ ```
65
+
66
+ For more information run
67
+
68
+ ```
69
+ trainer --help
70
+ ````
71
+
72
+ ### Thanks
73
+
74
+ After the [lobbying of @steipete](https://twitter.com/steipete/status/753662170848690176) and the comment
75
+
76
+ > How does Xcode Server parse the results?
77
+
78
+ I started investigating alternative approaches on how to parse test results.
data/bin/trainer ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push File.expand_path("../../lib", __FILE__)
3
+
4
+ require 'trainer'
5
+ require 'trainer/commands_generator'
6
+ Trainer::CommandsGenerator.start
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <% number_of_tests = 0 %>
3
+ <% number_of_failures = 0 %>
4
+ <% @results.each { |a| number_of_tests += a[:number_of_tests] } %>
5
+ <% @results.each { |a| number_of_failures += a[:number_of_failures] } %>
6
+
7
+ <testsuites tests="<%= number_of_tests %>" failures="<%= number_of_failures %>">
8
+ <% @results.each do |testsuite| %>
9
+ <testsuite name=<%= testsuite[:target_name].encode(:xml => :attr) %> tests="<%= testsuite[:number_of_tests] %>" failures="<%= testsuite[:number_of_failures] %>">
10
+ <% testsuite[:tests].each do |test| %>
11
+ <testcase classname=<%= test[:test_group].encode(:xml => :attr) %> name=<%= test[:name].encode(:xml => :attr) %>>
12
+ <% (test[:failures] || []).each do |failure| %>
13
+ <failure message=<%= failure[:failure_message].encode(:xml => :attr) %>>
14
+ </failure>
15
+ <% end %>
16
+ </testcase>
17
+ <% end %>
18
+ </testsuite>
19
+ <% end %>
20
+ </testsuites>
@@ -0,0 +1,43 @@
1
+ require 'commander'
2
+
3
+ HighLine.track_eof = false
4
+
5
+ module Trainer
6
+ class CommandsGenerator
7
+ include Commander::Methods
8
+
9
+ def self.start
10
+ self.new.run
11
+ end
12
+
13
+ def run
14
+ program :version, Trainer::VERSION
15
+ program :description, Trainer::DESCRIPTION
16
+ program :help, 'Author', 'Felix Krause <trainer@krausefx.com>'
17
+ program :help, 'Website', 'https://fastlane.tools'
18
+ program :help, 'GitHub', 'https://github.com/KrauseFx/trainer'
19
+ program :help_formatter, :compact
20
+
21
+ global_option('--verbose', 'Shows a more verbose output') { $verbose = true }
22
+
23
+ always_trace!
24
+
25
+ FastlaneCore::CommanderGenerator.new.generate(Trainer::Options.available_options)
26
+
27
+ command :run do |c|
28
+ c.syntax = 'trainer'
29
+ c.description = Trainer::DESCRIPTION
30
+
31
+ c.action do |args, options|
32
+ options = FastlaneCore::Configuration.create(Trainer::Options.available_options, options.__hash__)
33
+ FastlaneCore::PrintTable.print_values(config: options, title: "Summary for trainer #{Trainer::VERSION}") if $verbose
34
+ Trainer::TestParser.auto_convert(options[:path])
35
+ end
36
+ end
37
+
38
+ default_command :run
39
+
40
+ run!
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module Trainer
2
+ class JunitGenerator
3
+ attr_accessor :results
4
+
5
+ def initialize(results)
6
+ self.results = results
7
+ end
8
+
9
+ def generate
10
+ # JUnit file documentation: http://llg.cubic.org/docs/junit/
11
+ # And http://nelsonwells.net/2012/09/how-jenkins-ci-parses-and-displays-junit-output/
12
+ # And http://windyroad.com.au/dl/Open%20Source/JUnit.xsd
13
+
14
+ lib_path = FastlaneCore::Helper.gem_path("trainer")
15
+ xml_path = File.join(lib_path, "lib/assets/junit.xml.erb")
16
+ xml = ERB.new(File.read(xml_path), nil, '<>').result(binding) # http://www.rrn.dk/rubys-erb-templating-system
17
+
18
+ xml = xml.gsub('system_', 'system-').delete("\e") # Jenkins can not parse 'ESC' symbol
19
+
20
+ # We have to manuall clear empty lines
21
+ # They may contain white spaces
22
+ clean_xml = []
23
+ xml.each_line do |row|
24
+ clean_xml << row.delete("\n") if row.strip.to_s.length > 0
25
+ end
26
+ return clean_xml.join("\n")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ module Trainer
2
+ class Options
3
+ def self.available_options
4
+ @options ||= [
5
+ FastlaneCore::ConfigItem.new(key: :path,
6
+ short_option: "-p",
7
+ env_name: "trainer_PATH",
8
+ default_value: ".",
9
+ description: "Path to the directory that should be converted",
10
+ verify_block: proc do |value|
11
+ v = File.expand_path(value.to_s)
12
+ UI.user_error!("Path '#{v}' is not a directory or can't be found") unless File.directory?(v)
13
+ end)
14
+ ]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,113 @@
1
+ module Trainer
2
+ class TestParser
3
+ attr_accessor :data
4
+
5
+ attr_accessor :file_content
6
+
7
+ attr_accessor :raw_json
8
+
9
+ def self.auto_convert(containing_dir)
10
+ files = Dir["#{containing_dir}/**/Logs/Test/*TestSummaries.plist"]
11
+ files += Dir["#{containing_dir}/Test/*TestSummaries.plist"]
12
+ files += Dir["#{containing_dir}/*TestSummaries.plist"]
13
+
14
+ UI.user_error!("No test result files found in directory '#{containing_dir}'") if files.empty?
15
+
16
+ return files.collect do |path|
17
+ to_path = path.gsub(".plist", ".junit")
18
+ File.write(to_path, Trainer::TestParser.new(path).to_junit)
19
+ puts "Successfully generated '#{to_path}'"
20
+ to_path
21
+ end
22
+ end
23
+
24
+ def initialize(path)
25
+ path = File.expand_path(path)
26
+ UI.user_error!("File not found at path '#{path}'") unless File.exist?(path)
27
+
28
+ self.file_content = File.read(path)
29
+ self.raw_json = Plist.parse_xml(self.file_content)
30
+ return if self.raw_json["FormatVersion"].to_s.length == 0 # maybe that's a useless plist file
31
+
32
+ ensure_file_valid!
33
+ parse_content
34
+ end
35
+
36
+ # Returns the JUnit report as String
37
+ def to_junit
38
+ JunitGenerator.new(self.data).generate
39
+ end
40
+
41
+ private
42
+
43
+ def ensure_file_valid!
44
+ format_version = self.raw_json["FormatVersion"]
45
+ UI.user_error!("Format version '#{format_version}' is not supported") unless format_version == "1.2"
46
+ end
47
+
48
+ # Convert the Hashes and Arrays in something more useful
49
+ def parse_content
50
+ def unfold_tests(data)
51
+ # `data` looks like this
52
+ # => [{"Subtests"=>
53
+ # [{"Subtests"=>
54
+ # [{"Subtests"=>
55
+ # [{"TestIdentifier"=>"Unit/testExample()",
56
+ # "TestName"=>"testExample()",
57
+ # "TestObjectClass"=>"IDESchemeActionTestSummary",
58
+ # "TestStatus"=>"Success",
59
+ # "TestSummaryGUID"=>"4A24BFED-03E6-4FBE-BC5E-2D80023C06B4"},
60
+ # {"FailureSummaries"=>
61
+ # [{"FileName"=>"/Users/krausefx/Developer/themoji/Unit/Unit.swift",
62
+ # "LineNumber"=>34,
63
+ # "Message"=>"XCTAssertTrue failed - ",
64
+ # "PerformanceFailure"=>false}],
65
+ # "TestIdentifier"=>"Unit/testExample2()",
66
+
67
+ tests = []
68
+ data.each do |current_hash|
69
+ if current_hash["Subtests"]
70
+ tests += unfold_tests(current_hash["Subtests"])
71
+ end
72
+ if current_hash["TestStatus"]
73
+ tests << current_hash
74
+ end
75
+ end
76
+ return tests
77
+ end
78
+
79
+ self.data = self.raw_json["TestableSummaries"].collect do |testable_summary|
80
+ summary_row = {
81
+ project_path: testable_summary["ProjectPath"],
82
+ target_name: testable_summary["TargetName"],
83
+ test_name: testable_summary["TestName"],
84
+ tests: unfold_tests(testable_summary["Tests"]).collect do |current_test|
85
+ current_row = {
86
+ identifier: current_test["TestIdentifier"],
87
+ test_group: current_test["TestIdentifier"].split("/")[0..-2].join("."),
88
+ name: current_test["TestName"],
89
+ object_class: current_test["TestObjectClass"],
90
+ status: current_test["TestStatus"],
91
+ guid: current_test["TestSummaryGUID"]
92
+ }
93
+ if current_test["FailureSummaries"]
94
+ current_row[:failures] = current_test["FailureSummaries"].collect do |current_failure|
95
+ {
96
+ file_name: current_failure['FileName'],
97
+ line_number: current_failure['LineNumber'],
98
+ message: current_failure['Message'],
99
+ performance_failure: current_failure['PerformanceFailure'],
100
+ failure_message: "#{current_failure['Message']}#{current_failure['FileName']}:#{current_failure['LineNumber']}"
101
+ }
102
+ end
103
+ end
104
+ current_row
105
+ end
106
+ }
107
+ summary_row[:number_of_tests] = summary_row[:tests].count
108
+ summary_row[:number_of_failures] = summary_row[:tests].find_all { |a| (a[:failures] || []).count > 0 }.count
109
+ summary_row
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,4 @@
1
+ module Trainer
2
+ VERSION = "0.1.1"
3
+ DESCRIPTION = "Convert xcodebuild plist files to JUnit reports"
4
+ end
data/lib/trainer.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'fastlane_core'
2
+
3
+ require 'trainer/version'
4
+ require 'trainer/options'
5
+ require 'trainer/test_parser'
6
+ require 'trainer/junit_generator'
7
+
8
+ module Trainer
9
+ UI = FastlaneCore::UI
10
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trainer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Felix Krause
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fastlane_core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.48.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.48.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: plist
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 3.1.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: 4.0.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 3.1.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: 4.0.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: bundler
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rake
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: pry
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rspec
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: 3.1.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: 3.1.0
109
+ - !ruby/object:Gem::Dependency
110
+ name: rubocop
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: 0.38.0
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: 0.38.0
123
+ description: Convert xcodebuild plist files to JUnit reports
124
+ email:
125
+ - fastlane@krausefx.com
126
+ executables:
127
+ - trainer
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - LICENSE
132
+ - README.md
133
+ - bin/trainer
134
+ - lib/assets/junit.xml.erb
135
+ - lib/trainer.rb
136
+ - lib/trainer/commands_generator.rb
137
+ - lib/trainer/junit_generator.rb
138
+ - lib/trainer/options.rb
139
+ - lib/trainer/test_parser.rb
140
+ - lib/trainer/version.rb
141
+ homepage: https://fastlane.tools
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 2.0.0
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubyforge_project:
161
+ rubygems_version: 2.4.6
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Convert xcodebuild plist files to JUnit reports
165
+ test_files: []