learn-xcpretty 0.1.11
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 +18 -0
- data/.kick +17 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +152 -0
- data/CONTRIBUTING.md +60 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +61 -0
- data/README.md +143 -0
- data/Rakefile +24 -0
- data/assets/report.html.erb +155 -0
- data/bin/learn-xcpretty +80 -0
- data/features/custom_formatter.feature +15 -0
- data/features/fixtures/xcodebuild.log +5963 -0
- data/features/html_report.feature +40 -0
- data/features/json_compilation_database_report.feature +21 -0
- data/features/junit_report.feature +44 -0
- data/features/knock_format.feature +11 -0
- data/features/simple_format.feature +172 -0
- data/features/steps/formatting_steps.rb +268 -0
- data/features/steps/html_steps.rb +23 -0
- data/features/steps/json_steps.rb +37 -0
- data/features/steps/junit_steps.rb +38 -0
- data/features/steps/report_steps.rb +21 -0
- data/features/steps/xcpretty_steps.rb +31 -0
- data/features/support/env.rb +108 -0
- data/features/tap_format.feature +31 -0
- data/features/test_format.feature +39 -0
- data/features/xcpretty.feature +14 -0
- data/learn-xcpretty.gemspec +37 -0
- data/lib/xcpretty/ansi.rb +71 -0
- data/lib/xcpretty/formatters/formatter.rb +134 -0
- data/lib/xcpretty/formatters/knock.rb +34 -0
- data/lib/xcpretty/formatters/rspec.rb +27 -0
- data/lib/xcpretty/formatters/simple.rb +155 -0
- data/lib/xcpretty/formatters/tap.rb +39 -0
- data/lib/xcpretty/parser.rb +421 -0
- data/lib/xcpretty/printer.rb +20 -0
- data/lib/xcpretty/reporters/html.rb +73 -0
- data/lib/xcpretty/reporters/json_compilation_database.rb +58 -0
- data/lib/xcpretty/reporters/junit.rb +99 -0
- data/lib/xcpretty/reporters/learn.rb +154 -0
- data/lib/xcpretty/snippet.rb +34 -0
- data/lib/xcpretty/syntax.rb +20 -0
- data/lib/xcpretty/version.rb +3 -0
- data/lib/xcpretty.rb +39 -0
- data/spec/fixtures/NSStringTests.m +64 -0
- data/spec/fixtures/constants.rb +546 -0
- data/spec/fixtures/custom_formatter.rb +17 -0
- data/spec/fixtures/oneliner.m +1 -0
- data/spec/fixtures/raw_kiwi_compilation_fail.txt +24 -0
- data/spec/fixtures/raw_kiwi_fail.txt +1896 -0
- data/spec/fixtures/raw_specta_fail.txt +3110 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/matchers/colors.rb +20 -0
- data/spec/xcpretty/ansi_spec.rb +46 -0
- data/spec/xcpretty/formatters/formatter_spec.rb +113 -0
- data/spec/xcpretty/formatters/rspec_spec.rb +55 -0
- data/spec/xcpretty/formatters/simple_spec.rb +129 -0
- data/spec/xcpretty/parser_spec.rb +421 -0
- data/spec/xcpretty/printer_spec.rb +53 -0
- data/spec/xcpretty/snippet_spec.rb +39 -0
- data/spec/xcpretty/syntax_spec.rb +35 -0
- data/vendor/json_pure/COPYING +57 -0
- data/vendor/json_pure/LICENSE +340 -0
- data/vendor/json_pure/generator.rb +443 -0
- data/vendor/json_pure/parser.rb +364 -0
- metadata +261 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
require "xcpretty/ansi"
|
2
|
+
|
3
|
+
module XCPretty
|
4
|
+
|
5
|
+
class Printer
|
6
|
+
|
7
|
+
attr_reader :formatter
|
8
|
+
|
9
|
+
def initialize(params)
|
10
|
+
klass = params[:formatter]
|
11
|
+
@formatter = klass.new(params[:unicode], params[:colorize])
|
12
|
+
end
|
13
|
+
|
14
|
+
def pretty_print(text)
|
15
|
+
formatted_text = formatter.pretty_format(text)
|
16
|
+
STDOUT.print(formatted_text + formatter.optional_newline) unless formatted_text.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class HTML
|
3
|
+
|
4
|
+
include XCPretty::FormatMethods
|
5
|
+
FILEPATH = 'build/reports/tests.html'
|
6
|
+
TEMPLATE = File.expand_path('../../../../assets/report.html.erb', __FILE__)
|
7
|
+
|
8
|
+
def load_dependencies
|
9
|
+
unless @@loaded ||= false
|
10
|
+
require 'fileutils'
|
11
|
+
require 'pathname'
|
12
|
+
require 'erb'
|
13
|
+
@@loaded = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(options)
|
18
|
+
load_dependencies
|
19
|
+
@test_suites = {}
|
20
|
+
@filepath = options[:path] || FILEPATH
|
21
|
+
@parser = Parser.new(self)
|
22
|
+
@test_count = 0
|
23
|
+
@fail_count = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle(line)
|
27
|
+
@parser.parse(line)
|
28
|
+
end
|
29
|
+
|
30
|
+
def format_failing_test(suite, test_case, reason, file)
|
31
|
+
add_test(suite, {:name => test_case, :failing => true,
|
32
|
+
:reason => reason, :file => file, :snippet => formatted_snippet(file)})
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_passing_test(suite, test_case, time)
|
36
|
+
add_test(suite, {:name => test_case, :time => time})
|
37
|
+
end
|
38
|
+
|
39
|
+
def finish
|
40
|
+
FileUtils.mkdir_p(File.dirname(@filepath))
|
41
|
+
write_report
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def formatted_snippet(filepath)
|
47
|
+
snippet = Snippet.from_filepath(filepath)
|
48
|
+
Syntax.highlight(snippet, "-f html -O style=colorful -O noclasses")
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def add_test(suite_name, data)
|
53
|
+
@test_count += 1
|
54
|
+
@test_suites[suite_name] ||= {:tests => []}
|
55
|
+
@test_suites[suite_name][:tests] << data
|
56
|
+
if data[:failing]
|
57
|
+
@test_suites[suite_name][:failing] = true
|
58
|
+
@fail_count += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def write_report
|
63
|
+
File.open(@filepath, 'w') do |f|
|
64
|
+
# WAT: get rid of these locals. BTW Cucumber fails if you remove them
|
65
|
+
test_suites = @test_suites
|
66
|
+
fail_count = @fail_count
|
67
|
+
test_count = @test_count
|
68
|
+
erb = ERB.new(File.open(TEMPLATE, 'r').read)
|
69
|
+
f.write erb.result(binding)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class JSONCompilationDatabase
|
3
|
+
|
4
|
+
include XCPretty::FormatMethods
|
5
|
+
FILEPATH = 'build/reports/compilation_db.json'
|
6
|
+
|
7
|
+
def load_dependencies
|
8
|
+
unless @@loaded ||= false
|
9
|
+
require 'fileutils'
|
10
|
+
require 'pathname'
|
11
|
+
unless Object.const_defined?(:JSON)
|
12
|
+
begin
|
13
|
+
require 'json'
|
14
|
+
rescue LoadError
|
15
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'../../../vendor/json_pure/generator'))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@@loaded = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(options)
|
23
|
+
load_dependencies
|
24
|
+
@filepath = options[:path] || FILEPATH
|
25
|
+
@parser = Parser.new(self)
|
26
|
+
@compilation_units = []
|
27
|
+
@current_file = nil
|
28
|
+
@current_path = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle(line)
|
32
|
+
@parser.parse(line)
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_compile(file_name, file_path)
|
36
|
+
@current_file = file_name
|
37
|
+
@current_path = File.dirname(file_path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def format_compile_command(compiler_command)
|
41
|
+
@compilation_units << { :command => compiler_command,
|
42
|
+
:file => @current_file, :directory => @current_path }
|
43
|
+
end
|
44
|
+
|
45
|
+
def finish
|
46
|
+
FileUtils.mkdir_p(File.dirname(@filepath))
|
47
|
+
write_report
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def write_report
|
53
|
+
File.open(@filepath, 'w') do |f|
|
54
|
+
f.write(@compilation_units.to_json)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class JUnit
|
3
|
+
|
4
|
+
include XCPretty::FormatMethods
|
5
|
+
FILEPATH = 'build/reports/junit.xml'
|
6
|
+
|
7
|
+
def load_dependencies
|
8
|
+
unless @@loaded ||= false
|
9
|
+
require 'fileutils'
|
10
|
+
require 'pathname'
|
11
|
+
require 'rexml/document'
|
12
|
+
require 'rexml/formatters/pretty'
|
13
|
+
@@loaded = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(options)
|
18
|
+
load_dependencies
|
19
|
+
@filepath = options[:path] || FILEPATH
|
20
|
+
@directory = `pwd`.strip
|
21
|
+
@document = REXML::Document.new
|
22
|
+
@document << REXML::XMLDecl.new('1.0','UTF-8')
|
23
|
+
@document.add_element('testsuites')
|
24
|
+
@parser = Parser.new(self)
|
25
|
+
@total_tests = 0
|
26
|
+
@total_fails = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle(line)
|
30
|
+
@parser.parse(line)
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_passing_test(classname, test_case, time)
|
34
|
+
test_node = suite(classname).add_element('testcase')
|
35
|
+
test_node.attributes['classname'] = classname
|
36
|
+
test_node.attributes['name'] = test_case
|
37
|
+
test_node.attributes['time'] = time
|
38
|
+
@test_count += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def format_pending_test(classname, test_case)
|
42
|
+
test_node = suite(classname).add_element('testcase')
|
43
|
+
test_node.attributes['classname'] = classname
|
44
|
+
test_node.attributes['name'] = test_case
|
45
|
+
test_node.add_element('skipped')
|
46
|
+
@test_count += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def format_failing_test(classname, test_case, reason, file)
|
50
|
+
test_node = suite(classname).add_element('testcase')
|
51
|
+
test_node.attributes['classname'] = classname
|
52
|
+
test_node.attributes['name'] = test_case
|
53
|
+
fail_node = test_node.add_element('failure')
|
54
|
+
fail_node.attributes['message'] = reason
|
55
|
+
fail_node.text = file.sub(@directory + '/', '')
|
56
|
+
@test_count += 1
|
57
|
+
@fail_count += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def finish
|
61
|
+
set_test_counters
|
62
|
+
@document.root.attributes['tests'] = @total_tests
|
63
|
+
@document.root.attributes['failures'] = @total_fails
|
64
|
+
write_report_file
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def write_report_file
|
70
|
+
FileUtils.mkdir_p(File.dirname(@filepath))
|
71
|
+
formatter = REXML::Formatters::Pretty.new(2)
|
72
|
+
formatter.compact = true
|
73
|
+
output_file = File.open(@filepath, 'w+')
|
74
|
+
result = formatter.write(@document, output_file)
|
75
|
+
output_file.close
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
def suite(classname)
|
80
|
+
return @last_suite if @last_suite && @last_suite.attributes['name'] == classname
|
81
|
+
|
82
|
+
set_test_counters
|
83
|
+
@last_suite = @document.root.add_element('testsuite')
|
84
|
+
@last_suite.attributes['name'] = classname
|
85
|
+
@last_suite
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_test_counters
|
89
|
+
if @last_suite
|
90
|
+
@last_suite.attributes['tests'] = @test_count
|
91
|
+
@last_suite.attributes['failures'] = @fail_count
|
92
|
+
end
|
93
|
+
@total_fails += @fail_count || 0
|
94
|
+
@total_tests += @test_count || 0
|
95
|
+
@test_count = 0
|
96
|
+
@fail_count = 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class Learn
|
3
|
+
SERVICE_URL = 'http://ironbroker.flatironschool.com'
|
4
|
+
SERVICE_ENDPOINT = '/e/flatiron_xcpretty/build'
|
5
|
+
|
6
|
+
include XCPretty::FormatMethods
|
7
|
+
|
8
|
+
def load_dependencies
|
9
|
+
unless @@loaded ||= false
|
10
|
+
require 'fileutils'
|
11
|
+
require 'pathname'
|
12
|
+
require 'json'
|
13
|
+
require 'faraday'
|
14
|
+
require 'netrc'
|
15
|
+
require 'git'
|
16
|
+
require 'oj'
|
17
|
+
@@loaded = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
load_dependencies
|
23
|
+
@formatted_output = {
|
24
|
+
username: UsernameParser.get_username,
|
25
|
+
github_user_id: UserIdParser.get_user_id,
|
26
|
+
repo_name: RepoParser.get_repo,
|
27
|
+
build: {
|
28
|
+
test_suite: [{
|
29
|
+
framework: 'xcpretty',
|
30
|
+
formatted_output: [],
|
31
|
+
duration: 0.0,
|
32
|
+
build_output: [],
|
33
|
+
}]
|
34
|
+
},
|
35
|
+
total_count: 0,
|
36
|
+
passing_count: 0,
|
37
|
+
failure_count: 0
|
38
|
+
}
|
39
|
+
|
40
|
+
@parser = Parser.new(self)
|
41
|
+
|
42
|
+
@connection = Faraday.new(url: SERVICE_URL) do |faraday|
|
43
|
+
faraday.adapter Faraday.default_adapter
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle(line)
|
48
|
+
@parser.parse(line)
|
49
|
+
end
|
50
|
+
|
51
|
+
def format_passing_test(classname, test_case, time)
|
52
|
+
pass = {classname: classname, name: test_case, time: time}
|
53
|
+
@formatted_output[:build][:test_suite][0][:formatted_output] << pass
|
54
|
+
@formatted_output[:total_count] += 1
|
55
|
+
@formatted_output[:passing_count] += 1
|
56
|
+
end
|
57
|
+
|
58
|
+
def format_failing_test(classname, test_case, reason, file)
|
59
|
+
failure = {classname: classname, name: test_case, file: file, reason: reason}
|
60
|
+
@formatted_output[:build][:test_suite][0][:formatted_output] << failure
|
61
|
+
@formatted_output[:total_count] += 1
|
62
|
+
@formatted_output[:failure_count] += 1
|
63
|
+
end
|
64
|
+
|
65
|
+
def finish
|
66
|
+
write_report_file
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def write_report_file
|
72
|
+
@connection.post do |req|
|
73
|
+
req.url SERVICE_ENDPOINT
|
74
|
+
req.headers['Content-Type'] = 'application/json'
|
75
|
+
req.body = @formatted_output.to_json
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class UsernameParser
|
81
|
+
def self.get_username
|
82
|
+
parser = NetrcInteractor.new
|
83
|
+
username = parser.username
|
84
|
+
|
85
|
+
if !username
|
86
|
+
print "Enter your github username: "
|
87
|
+
username = gets.strip
|
88
|
+
user_id = GitHubInteractor.get_user_id_for(username)
|
89
|
+
parser.write(username, user_id)
|
90
|
+
end
|
91
|
+
|
92
|
+
username
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class UserIdParser
|
97
|
+
def self.get_user_id
|
98
|
+
parser = NetrcInteractor.new
|
99
|
+
user_id = parser.user_id
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class GitHubInteractor
|
104
|
+
attr_reader :username, :user_id
|
105
|
+
|
106
|
+
def self.get_user_id_for(username)
|
107
|
+
new(username).get_user_id
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(username)
|
111
|
+
@username = username
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_user_id
|
115
|
+
@user_id ||= Oj.load(
|
116
|
+
open("https://api.github.com/users/#{username}").read,
|
117
|
+
symbol_keys: true
|
118
|
+
)[:id]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class NetrcInteractor
|
123
|
+
attr_reader :username, :user_id, :netrc
|
124
|
+
|
125
|
+
def initialize
|
126
|
+
@netrc = Netrc.read
|
127
|
+
@username, @user_id = netrc["flatiron-push"]
|
128
|
+
end
|
129
|
+
|
130
|
+
def write(username, user_id)
|
131
|
+
netrc["flatiron-push"] = username, user_id
|
132
|
+
netrc.save
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class RepoParser
|
137
|
+
def self.get_repo
|
138
|
+
begin
|
139
|
+
repo = Git.open(File.expand_path(".", Dir.pwd))
|
140
|
+
rescue
|
141
|
+
puts "Not a valid Git repository"
|
142
|
+
die
|
143
|
+
end
|
144
|
+
|
145
|
+
url = repo.remote.url
|
146
|
+
|
147
|
+
repo_name = url.match(/(?:https:\/\/|git@).*\/(.+)(?:\.git)/)[1]
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.die
|
151
|
+
exit
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class Snippet
|
3
|
+
|
4
|
+
def self.from_filepath(filepath)
|
5
|
+
path, line = filepath.split(':')
|
6
|
+
file = File.open(path)
|
7
|
+
|
8
|
+
text = read_snippet(file, line)
|
9
|
+
|
10
|
+
file.close
|
11
|
+
text
|
12
|
+
rescue
|
13
|
+
''
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.read_snippet(file, around_line)
|
20
|
+
text = ""
|
21
|
+
starting_position = around_line.to_i - 2
|
22
|
+
starting_position.times { file.gets }
|
23
|
+
3.times { text += readline(file) }
|
24
|
+
text
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.readline(file)
|
28
|
+
file.gets
|
29
|
+
$_ || ''
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module XCPretty
|
2
|
+
class Syntax
|
3
|
+
|
4
|
+
def self.highlight(code, options="")
|
5
|
+
pygments_available? ? pygmentize(code, options) : code
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def self.pygments_available?
|
12
|
+
@available = system('which pygmentize > /dev/null') if @available.nil?
|
13
|
+
@available
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.pygmentize(code, options)
|
17
|
+
`echo "#{code}" | pygmentize -l objc #{options}`
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|