learn-xcpretty 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|