learn-xcpretty 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.kick +17 -0
  4. data/.travis.yml +18 -0
  5. data/CHANGELOG.md +152 -0
  6. data/CONTRIBUTING.md +60 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +61 -0
  9. data/README.md +143 -0
  10. data/Rakefile +24 -0
  11. data/assets/report.html.erb +155 -0
  12. data/bin/learn-xcpretty +80 -0
  13. data/features/custom_formatter.feature +15 -0
  14. data/features/fixtures/xcodebuild.log +5963 -0
  15. data/features/html_report.feature +40 -0
  16. data/features/json_compilation_database_report.feature +21 -0
  17. data/features/junit_report.feature +44 -0
  18. data/features/knock_format.feature +11 -0
  19. data/features/simple_format.feature +172 -0
  20. data/features/steps/formatting_steps.rb +268 -0
  21. data/features/steps/html_steps.rb +23 -0
  22. data/features/steps/json_steps.rb +37 -0
  23. data/features/steps/junit_steps.rb +38 -0
  24. data/features/steps/report_steps.rb +21 -0
  25. data/features/steps/xcpretty_steps.rb +31 -0
  26. data/features/support/env.rb +108 -0
  27. data/features/tap_format.feature +31 -0
  28. data/features/test_format.feature +39 -0
  29. data/features/xcpretty.feature +14 -0
  30. data/learn-xcpretty.gemspec +37 -0
  31. data/lib/xcpretty/ansi.rb +71 -0
  32. data/lib/xcpretty/formatters/formatter.rb +134 -0
  33. data/lib/xcpretty/formatters/knock.rb +34 -0
  34. data/lib/xcpretty/formatters/rspec.rb +27 -0
  35. data/lib/xcpretty/formatters/simple.rb +155 -0
  36. data/lib/xcpretty/formatters/tap.rb +39 -0
  37. data/lib/xcpretty/parser.rb +421 -0
  38. data/lib/xcpretty/printer.rb +20 -0
  39. data/lib/xcpretty/reporters/html.rb +73 -0
  40. data/lib/xcpretty/reporters/json_compilation_database.rb +58 -0
  41. data/lib/xcpretty/reporters/junit.rb +99 -0
  42. data/lib/xcpretty/reporters/learn.rb +154 -0
  43. data/lib/xcpretty/snippet.rb +34 -0
  44. data/lib/xcpretty/syntax.rb +20 -0
  45. data/lib/xcpretty/version.rb +3 -0
  46. data/lib/xcpretty.rb +39 -0
  47. data/spec/fixtures/NSStringTests.m +64 -0
  48. data/spec/fixtures/constants.rb +546 -0
  49. data/spec/fixtures/custom_formatter.rb +17 -0
  50. data/spec/fixtures/oneliner.m +1 -0
  51. data/spec/fixtures/raw_kiwi_compilation_fail.txt +24 -0
  52. data/spec/fixtures/raw_kiwi_fail.txt +1896 -0
  53. data/spec/fixtures/raw_specta_fail.txt +3110 -0
  54. data/spec/spec_helper.rb +6 -0
  55. data/spec/support/matchers/colors.rb +20 -0
  56. data/spec/xcpretty/ansi_spec.rb +46 -0
  57. data/spec/xcpretty/formatters/formatter_spec.rb +113 -0
  58. data/spec/xcpretty/formatters/rspec_spec.rb +55 -0
  59. data/spec/xcpretty/formatters/simple_spec.rb +129 -0
  60. data/spec/xcpretty/parser_spec.rb +421 -0
  61. data/spec/xcpretty/printer_spec.rb +53 -0
  62. data/spec/xcpretty/snippet_spec.rb +39 -0
  63. data/spec/xcpretty/syntax_spec.rb +35 -0
  64. data/vendor/json_pure/COPYING +57 -0
  65. data/vendor/json_pure/LICENSE +340 -0
  66. data/vendor/json_pure/generator.rb +443 -0
  67. data/vendor/json_pure/parser.rb +364 -0
  68. metadata +261 -0
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+ require 'xcpretty/ansi'
3
+ require 'xcpretty/parser'
4
+
5
+ module XCPretty
6
+
7
+ # Making a new formatter is easy.
8
+ # Just make a subclass of Formatter, and override any of these methods.
9
+ module FormatMethods
10
+ EMPTY = ''.freeze
11
+
12
+ def format_analyze(file_name, file_path); EMPTY; end
13
+ def format_build_target(target, project, configuration); EMPTY; end
14
+ def format_check_dependencies; EMPTY; end
15
+ def format_clean(project, target, configuration); EMPTY; end
16
+ def format_clean_target(target, project, configuration); EMPTY; end
17
+ def format_clean_remove; EMPTY; end
18
+ def format_compile(file_name, file_path); EMPTY; end
19
+ def format_compile_command(compiler_command); EMPTY; end
20
+ def format_compile_xib(file_name, file_path); EMPTY; end
21
+ def format_copy_strings_file(file_name); EMPTY; end
22
+ def format_cpresource(file); EMPTY; end
23
+ def format_generate_dsym(dsym); EMPTY; end
24
+ def format_linking(file, build_variant, arch); EMPTY; end
25
+ def format_libtool(library); EMPTY; end
26
+ def format_passing_test(suite, test, time); EMPTY; end
27
+ def format_pending_test(suite, test); EMPTY; end
28
+ def format_failing_test(suite, test, time, file_path); EMPTY; end
29
+ def format_process_pch(file); EMPTY; end
30
+ def format_phase_script_execution(script_name); EMPTY; end
31
+ def format_process_info_plist(file_name, file_path); EMPTY; end
32
+ def format_codesign(file); EMPTY; end
33
+ def format_preprocess(file); EMPTY; end
34
+ def format_pbxcp(file); EMPTY; end
35
+ def format_test_run_started(name); EMPTY; end
36
+ def format_test_run_finished(name, time); EMPTY; end
37
+ def format_test_suite_started(name); EMPTY; end
38
+ def format_test_summary(message, failures_per_suite); EMPTY; end
39
+ def format_touch(file_path, file_name); EMPTY; end
40
+ def format_tiffutil(file); EMPTY; end
41
+
42
+ # COMPILER / LINKER ERRORS
43
+ def format_compile_error(file_name, file_path, reason,
44
+ line, cursor); EMPTY; end
45
+ def format_error(message); EMPTY; end
46
+ def format_undefined_symbols(message, symbol, reference); EMPTY; end
47
+ def format_duplicate_symbols(message, file_paths); EMPTY; end
48
+ end
49
+
50
+ class Formatter
51
+
52
+ include ANSI
53
+ include FormatMethods
54
+
55
+ attr_reader :parser
56
+
57
+ def initialize(use_unicode, colorize)
58
+ @use_unicode = use_unicode
59
+ @colorize = colorize
60
+ @parser = Parser.new(self)
61
+ end
62
+
63
+ # Override if you want to catch something specific with your regex
64
+ def pretty_format(text)
65
+ parser.parse(text)
66
+ end
67
+
68
+ # If you want to print inline, override #optional_newline with ''
69
+ def optional_newline
70
+ "\n"
71
+ end
72
+
73
+ def use_unicode?
74
+ !!@use_unicode
75
+ end
76
+
77
+ # Will be printed by default. Override with '' if you don't want summary
78
+ def format_test_summary(executed_message, failures_per_suite)
79
+ failures = format_failures(failures_per_suite)
80
+ final_message = failures.empty? ? green(executed_message) : red(executed_message)
81
+
82
+ text = [failures, final_message].join("\n\n\n").strip
83
+ "\n\n#{text}"
84
+ end
85
+
86
+ ERROR = "⌦"
87
+ ASCII_ERROR = "[!]"
88
+
89
+ def format_error(message)
90
+ "\n#{red(error_symbol + " " + message)}\n\n"
91
+ end
92
+
93
+ def format_compile_error(file, file_path, reason, line, cursor)
94
+ "\n#{red(error_symbol + " ")}#{file_path}: #{red(reason)}\n\n" +
95
+ "#{line}\n#{cyan(cursor)}\n\n"
96
+ end
97
+
98
+ def format_undefined_symbols(message, symbol, reference)
99
+ "\n#{red(error_symbol + " " + message)}\n" +
100
+ "> Symbol: #{symbol}\n" +
101
+ "> Referenced from: #{reference}\n\n"
102
+ end
103
+
104
+ def format_duplicate_symbols(message, file_paths)
105
+ "\n#{red(error_symbol + " " + message)}\n" +
106
+ "> #{file_paths.map { |path| path.split('/').last }.join("\n> ")}\n"
107
+ end
108
+
109
+
110
+ private
111
+
112
+ def format_failures(failures_per_suite)
113
+ failures_per_suite.map do |suite, failures|
114
+ formatted_failures = failures.map do |failure|
115
+ format_failure(failure)
116
+ end.join("\n\n")
117
+
118
+ "\n#{suite}\n#{formatted_failures}"
119
+ end.join("\n")
120
+ end
121
+
122
+ def format_failure(f)
123
+ " #{f[:test_case]}, #{red(f[:reason])}\n #{cyan(f[:file_path])}\n" +
124
+ " ```\n" +
125
+ Syntax.highlight(Snippet.from_filepath(f[:file_path])) +
126
+ " ```"
127
+ end
128
+
129
+ def error_symbol
130
+ use_unicode? ? ERROR : ASCII_ERROR
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,34 @@
1
+ module XCPretty
2
+
3
+ class Knock < Formatter
4
+
5
+ FAIL = 'not ok'
6
+ PASS = 'ok'
7
+
8
+ def format_passing_test(suite, test_case, time)
9
+ "#{PASS} - #{test_case}"
10
+ end
11
+
12
+ def format_failing_test(test_suite, test_case, reason, file)
13
+ "#{FAIL} - #{test_case}: FAILED" +
14
+ format_failure_diagnostics(test_suite, test_case, reason, file)
15
+ end
16
+
17
+ def format_test_summary(executed_message, failures_per_suite)
18
+ ''
19
+ end
20
+
21
+ def format_failure_diagnostics(test_suite, test_case, reason, file)
22
+ format_diagnostics(reason) +
23
+ format_diagnostics(" #{file}: #{test_suite} - #{test_case}")
24
+ end
25
+
26
+ private
27
+
28
+ def format_diagnostics(text)
29
+ "\n# #{text}"
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,27 @@
1
+ module XCPretty
2
+
3
+ class RSpec < Formatter
4
+
5
+ FAIL = "F"
6
+ PASS = "."
7
+ PENDING = "P"
8
+
9
+ def optional_newline
10
+ ''
11
+ end
12
+
13
+ def format_passing_test(suite, test_case, time)
14
+ green(PASS)
15
+ end
16
+
17
+ def format_failing_test(test_suite, test_case, reason, file)
18
+ red(FAIL)
19
+ end
20
+
21
+ def format_pending_test(suite, test_case)
22
+ yellow(PENDING)
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,155 @@
1
+ # encoding: utf-8
2
+ require 'shellwords'
3
+
4
+ module XCPretty
5
+
6
+ class Simple < Formatter
7
+
8
+ PASS = "✓"
9
+ FAIL = "✗"
10
+ PENDING = "⧖"
11
+
12
+ ASCII_PASS = "."
13
+ ASCII_FAIL = "x"
14
+ COMPLETION = "▸"
15
+ ASCII_PENDING = "P"
16
+ ASCII_COMPLETION = ">"
17
+
18
+ INDENT = " "
19
+
20
+ def format_analyze(file_name, file_path)
21
+ format("Analyzing", file_name)
22
+ end
23
+
24
+ def format_build_target(target, project, configuration)
25
+ format("Building", "#{project}/#{target} [#{configuration}]")
26
+ end
27
+
28
+ def format_clean_target(target, project, configuration)
29
+ format("Cleaning", "#{project}/#{target} [#{configuration}]")
30
+ end
31
+
32
+ def format_compile(file_name, file_path)
33
+ format("Compiling", file_name)
34
+ end
35
+
36
+ def format_compile_xib(file_name, file_path)
37
+ format("Compiling", file_name)
38
+ end
39
+
40
+ def format_copy_strings_file(file)
41
+ format("Copying", file)
42
+ end
43
+
44
+ def format_cpresource(resource)
45
+ format("Copying", resource)
46
+ end
47
+
48
+ def format_generate_dsym(dsym)
49
+ format("Generating '#{dsym}'")
50
+ end
51
+
52
+ def format_libtool(library)
53
+ format("Building library", library)
54
+ end
55
+
56
+ def format_linking(target, build_variants, arch)
57
+ format("Linking", target)
58
+ end
59
+
60
+ def format_failing_test(suite, test_case, reason, file)
61
+ INDENT + format_test("#{test_case}, #{reason}", :fail)
62
+ end
63
+
64
+ def format_passing_test(suite, test_case, time)
65
+ INDENT + format_test("#{test_case} (#{colored_time(time)} seconds)", :pass)
66
+ end
67
+
68
+ def format_pending_test(suite, test_case)
69
+ INDENT + format_test("#{test_case} [PENDING]", :pending)
70
+ end
71
+
72
+ def format_phase_script_execution(script_name)
73
+ format("Running script", "'#{script_name}'")
74
+ end
75
+
76
+ def format_process_info_plist(file_name, file_path)
77
+ format("Processing", file_name)
78
+ end
79
+
80
+ def format_process_pch(file)
81
+ format("Precompiling", file)
82
+ end
83
+
84
+ def format_codesign(file)
85
+ format("Signing", file)
86
+ end
87
+
88
+ def format_preprocess(file)
89
+ format("Preprocessing", file)
90
+ end
91
+
92
+ def format_pbxcp(file)
93
+ format("Copying", file)
94
+ end
95
+
96
+ def format_test_run_started(name)
97
+ heading("Test Suite", name, "started")
98
+ end
99
+
100
+ def format_test_suite_started(name)
101
+ heading("", name, "")
102
+ end
103
+
104
+ def format_touch(file_path, file_name)
105
+ format("Touching", file_name)
106
+ end
107
+
108
+ def format_tiffutil(file_name)
109
+ format("Validating", file_name)
110
+ end
111
+
112
+ private
113
+
114
+ def heading(prefix, text, description)
115
+ [prefix, white(text), description].join(" ").strip
116
+ end
117
+
118
+ def format(command, argument_text="", success=true)
119
+ [status_symbol(success ? :completion : :fail), white(command), argument_text].join(" ").strip
120
+ end
121
+
122
+ def format_test(test_case, status)
123
+ [status_symbol(status), test_case].join(" ").strip
124
+ end
125
+
126
+ def status_symbol(status)
127
+ case status
128
+ when :pass
129
+ green(use_unicode? ? PASS : ASCII_PASS)
130
+ when :fail
131
+ red(use_unicode? ? FAIL : ASCII_FAIL)
132
+ when :pending
133
+ yellow(use_unicode? ? PENDING : ASCII_PENDING)
134
+ when :error
135
+ red(use_unicode? ? ERROR : ASCII_ERROR)
136
+ when :completion
137
+ yellow(use_unicode? ? COMPLETION : ASCII_COMPLETION)
138
+ else
139
+ ""
140
+ end
141
+ end
142
+
143
+ def colored_time(time)
144
+ case time.to_f
145
+ when 0..0.025
146
+ time
147
+ when 0.026..0.100
148
+ yellow(time)
149
+ else
150
+ red(time)
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,39 @@
1
+ module XCPretty
2
+
3
+ class TestAnything < Knock
4
+
5
+ attr_reader :counter
6
+
7
+ def initialize unicode, color
8
+ super
9
+ @counter = 0
10
+ end
11
+
12
+ def format_passing_test(suite, test_case, time)
13
+ increment_counter
14
+ "#{PASS} #{counter} - #{test_case}"
15
+ end
16
+
17
+ def format_failing_test(test_suite, test_case, reason, file)
18
+ increment_counter
19
+ "#{FAIL} #{counter} - #{test_case}" +
20
+ format_failure_diagnostics(test_suite, test_case, reason, file)
21
+ end
22
+
23
+ def format_pending_test(test_suite, test_case)
24
+ increment_counter
25
+ "#{FAIL} #{counter} - #{test_case} # TODO Not written yet"
26
+ end
27
+
28
+ def format_test_summary(executed_message, failures_per_suite)
29
+ counter > 0 ? "1..#{counter}" : ''
30
+ end
31
+
32
+ private
33
+
34
+ def increment_counter
35
+ @counter += 1
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,421 @@
1
+ module XCPretty
2
+
3
+ module Matchers
4
+
5
+ # @regex Captured groups
6
+ # $1 file_path
7
+ # $2 file_name
8
+ ANALYZE_MATCHER = /^Analyze(?:Shallow)?\s(.*\/(.*\.m))*/
9
+
10
+ # @regex Captured groups
11
+ # $1 target
12
+ # $2 project
13
+ # $3 configuration
14
+ BUILD_TARGET_MATCHER = /^=== BUILD TARGET\s(.*)\sOF PROJECT\s(.*)\sWITH.*CONFIGURATION\s(.*)\s===/
15
+
16
+ # @regex Nothing returned here for now
17
+ CHECK_DEPENDENCIES_MATCHER = /^Check dependencies/
18
+
19
+ # @regex Nothing returned here for now
20
+ CLEAN_REMOVE_MATCHER = /^Clean.Remove/
21
+
22
+ # @regex Captured groups
23
+ # $1 target
24
+ # $2 project
25
+ # $3 configuration
26
+ CLEAN_TARGET_MATCHER = /^=== CLEAN TARGET\s(.*)\sOF PROJECT\s(.*)\sWITH CONFIGURATION\s(.*)\s===/
27
+
28
+ # @regex Captured groups
29
+ # $1 = file
30
+ CODESIGN_MATCHER = /^CodeSign\s((?:\\ |[^ ])*)$/
31
+
32
+ # @regex Captured groups
33
+ # $1 = file
34
+ CODESIGN_FRAMEWORK_MATCHER = /^CodeSign\s((?:\\ |[^ ])*.framework)\/Versions/
35
+
36
+ # @regex Captured groups
37
+ # $1 file_path
38
+ # $2 file_name (e.g. KWNull.m)
39
+ COMPILE_MATCHER = /^CompileC\s.*\s(.*\/(.*\.(?:m|mm|c|cc|cpp|cxx)))\s.*/
40
+
41
+ # @regex Captured groups
42
+ # $1 compiler_command
43
+ COMPILE_COMMAND_MATCHER = /^\s*(.*\/usr\/bin\/clang\s.*\.o)$/
44
+
45
+ # @regex Captured groups
46
+ # $1 file_path
47
+ # $2 file_name (e.g. MainMenu.xib)
48
+ COMPILE_XIB_MATCHER = /^CompileXIB\s(.*\/(.*\.xib))/
49
+
50
+ # @regex Captured groups
51
+ # $1 file
52
+ COPY_STRINGS_MATCHER = /^CopyStringsFile.*\/(.*.strings)/
53
+
54
+ # @regex Captured groups
55
+ # $1 resource
56
+ CPRESOURCE_MATCHER = /^CpResource\s(.*)\s\//
57
+
58
+ # @regex Captured groups
59
+ #
60
+ EXECUTED_MATCHER = /^\s*Executed/
61
+
62
+ # @regex Captured groups
63
+ # $1 = file
64
+ # $2 = test_suite
65
+ # $3 = test_case
66
+ # $4 = reason
67
+ FAILING_TEST_MATCHER = /^\s*(.+:\d+):\serror:\s[\+\-]\[(.*)\s(.*)\]\s:(?:\s'.*'\s\[FAILED\],)?\s(.*)/
68
+
69
+ # @regex Captured groups
70
+ # $1 = dsym
71
+ GENERATE_DSYM_MATCHER = /^GenerateDSYMFile \/.*\/(.*\.dSYM)/
72
+
73
+ # @regex Captured groups
74
+ # $1 = library
75
+ LIBTOOL_MATCHER = /^Libtool.*\/(.*\.a)/
76
+
77
+ # @regex Captured groups
78
+ # $1 = target
79
+ # $2 = build_variants (normal, profile, debug)
80
+ # $3 = architecture
81
+ LINKING_MATCHER = /^Ld \/.*\/(.*) (.*) (.*)$/
82
+
83
+ # @regex Captured groups
84
+ # $1 = suite
85
+ # $2 = test_case
86
+ # $3 = time
87
+ PASSING_TEST_MATCHER = /^\s*Test Case\s'-\[(.*)\s(.*)\]'\spassed\s\((\d*\.\d{3})\sseconds\)/
88
+
89
+ # @regex Captured groups
90
+ # $1 = suite
91
+ # $2 = test_case
92
+ PENDING_TEST_MATCHER = /^Test Case\s'-\[(.*)\s(.*)PENDING\]'\spassed/
93
+
94
+ # @regex Captured groups
95
+ # $1 = script_name
96
+ PHASE_SCRIPT_EXECUTION_MATCHER = /^PhaseScriptExecution\s(.*)\s\//
97
+
98
+ # @regex Captured groups
99
+ # $1 = file
100
+ PROCESS_PCH_MATCHER = /^ProcessPCH\s.*\s(.*.pch)/
101
+
102
+ # @regex Captured groups
103
+ # $1 = file
104
+ PREPROCESS_MATCHER = /^Preprocess\s(?:(?:\\ |[^ ])*)\s((?:\\ |[^ ])*)$/
105
+
106
+ # @regex Captured groups
107
+ # $1 = file
108
+ PBXCP_MATCHER = /^PBXCp\s((?:\\ |[^ ])*)/
109
+
110
+ # @regex Captured groups
111
+ # $1 = file
112
+ PROCESS_INFO_PLIST_MATCHER = /^ProcessInfoPlistFile\s.*\.plist\s(.*\/+(.*\.plist))/
113
+
114
+ # @regex Captured groups
115
+ # $1 = suite
116
+ # $2 = time
117
+ TESTS_RUN_COMPLETION_MATCHER = /^\s*Test Suite '(?:.*\/)?(.*[ox]ctest.*)' finished at (.*)/
118
+
119
+ # @regex Captured groups
120
+ # $1 = suite
121
+ # $2 = time
122
+ TESTS_RUN_START_MATCHER = /^\s*Test Suite '(?:.*\/)?(.*[ox]ctest.*)' started at(.*)/
123
+
124
+ # @regex Captured groups
125
+ # $1 test suite name
126
+ TEST_SUITE_START_MATCHER = /^\s*Test Suite '(.*)' started at/
127
+
128
+ # @regex Captured groups
129
+ # $1 file_name
130
+ TIFFUTIL_MATCHER = /^TiffUtil\s(.*)/
131
+
132
+ # @regex Captured groups
133
+ # $1 file_path
134
+ # $2 file_name
135
+ TOUCH_MATCHER = /^Touch\s(.*\/([\w+\.]+))/
136
+
137
+ module Errors
138
+ # @regex Captured groups
139
+ # $1 = whole error
140
+ CLANG_ERROR_MATCHER = /^(clang: error:.*)$/
141
+
142
+ # @regex Captured groups
143
+ # $1 = whole error
144
+ CODESIGN_ERROR_MATCHER = /^(Code\s?Sign error:.*)$/
145
+
146
+ # @regex Captured groups
147
+ # $1 = file_path
148
+ # $2 = file_name
149
+ # $3 = reason
150
+ COMPILE_ERROR_MATCHER = /^(\/.+\/(.*):.*:.*):(?:\sfatal)?\serror:\s(.*)$/
151
+
152
+ # @regex Captured groups
153
+ # $1 cursor (with whitespaces and tildes)
154
+ CURSOR_MATCHER = /^([\s~]*\^[\s~]*)$/
155
+
156
+ # @regex Captured groups
157
+ # $1 = whole error.
158
+ # it varies a lot, not sure if it makes sense to catch everything separately
159
+ FATAL_ERROR_MATCHER = /^(fatal error:.*)$/
160
+
161
+ # @regex Captured groups
162
+ # $1 = whole error
163
+ LD_ERROR_MATCHER = /^(ld:.*not found for.*)/
164
+
165
+ # @regex Captured groups
166
+ # $1 file path
167
+ LINKER_DUPLICATE_SYMBOLS_LOCATION_MATCHER = /^\s+(\/.*\.o[\)]?)$/
168
+
169
+ # @regex Captured groups
170
+ # $1 reason
171
+ LINKER_DUPLICATE_SYMBOLS_MATCHER = /^(duplicate symbol .*):$/
172
+
173
+ # @regex Captured groups
174
+ # $1 symbol location
175
+ LINKER_UNDEFINED_SYMBOL_LOCATION_MATCHER = /^(.* in .*\.o)$/
176
+
177
+ # @regex Captured groups
178
+ # $1 reason
179
+ LINKER_UNDEFINED_SYMBOLS_MATCHER = /^(Undefined symbols for architecture .*):$/
180
+
181
+ # @regex Captured groups
182
+ PODS_ERROR_MATCHER = /^error:\s(.*)/
183
+
184
+ # @regex Captured groups
185
+ # $1 = reference
186
+ SYMBOL_REFERENCED_FROM_MATCHER = /\s+"(.*)", referenced from:$/
187
+ end
188
+ end
189
+
190
+ class Parser
191
+
192
+ include Matchers
193
+ include Matchers::Errors
194
+
195
+ attr_reader :formatter
196
+
197
+ def initialize(formatter)
198
+ @formatter = formatter
199
+ end
200
+
201
+ def parse(text)
202
+ update_test_state(text)
203
+ update_error_state(text)
204
+ update_linker_failure_state(text)
205
+
206
+ return format_compile_error if should_format_error?
207
+ return format_undefined_symbols if should_format_undefined_symbols?
208
+ return format_duplicate_symbols if should_format_duplicate_symbols?
209
+
210
+ case text
211
+ when ANALYZE_MATCHER
212
+ formatter.format_analyze($2, $1)
213
+ when BUILD_TARGET_MATCHER
214
+ formatter.format_build_target($1, $2, $3)
215
+ when CLEAN_REMOVE_MATCHER
216
+ formatter.format_clean_remove
217
+ when CLEAN_TARGET_MATCHER
218
+ formatter.format_clean_target($1, $2, $3)
219
+ when COPY_STRINGS_MATCHER
220
+ formatter.format_copy_strings_file($1)
221
+ when CHECK_DEPENDENCIES_MATCHER
222
+ formatter.format_check_dependencies
223
+ when CLANG_ERROR_MATCHER
224
+ formatter.format_error($1)
225
+ when CODESIGN_FRAMEWORK_MATCHER
226
+ formatter.format_codesign($1)
227
+ when CODESIGN_MATCHER
228
+ formatter.format_codesign($1)
229
+ when CODESIGN_ERROR_MATCHER
230
+ formatter.format_error($1)
231
+ when COMPILE_MATCHER
232
+ formatter.format_compile($2, $1)
233
+ when COMPILE_COMMAND_MATCHER
234
+ formatter.format_compile_command($1)
235
+ when COMPILE_XIB_MATCHER
236
+ formatter.format_compile_xib($2, $1)
237
+ when CPRESOURCE_MATCHER
238
+ formatter.format_cpresource($1)
239
+ when EXECUTED_MATCHER
240
+ format_summary_if_needed(text)
241
+ when FAILING_TEST_MATCHER
242
+ formatter.format_failing_test($2, $3, $4, $1)
243
+ when FATAL_ERROR_MATCHER
244
+ formatter.format_error($1)
245
+ when GENERATE_DSYM_MATCHER
246
+ formatter.format_generate_dsym($1)
247
+ when LD_ERROR_MATCHER
248
+ formatter.format_error($1)
249
+ when LIBTOOL_MATCHER
250
+ formatter.format_libtool($1)
251
+ when LINKING_MATCHER
252
+ formatter.format_linking($1, $2, $3)
253
+ when PENDING_TEST_MATCHER
254
+ formatter.format_pending_test($1, $2)
255
+ when PASSING_TEST_MATCHER
256
+ formatter.format_passing_test($1, $2, $3)
257
+ when PODS_ERROR_MATCHER
258
+ formatter.format_error($1)
259
+ when PROCESS_INFO_PLIST_MATCHER
260
+ formatter.format_process_info_plist(*unescaped($2, $1))
261
+ when PHASE_SCRIPT_EXECUTION_MATCHER
262
+ formatter.format_phase_script_execution(*unescaped($1))
263
+ when PROCESS_PCH_MATCHER
264
+ formatter.format_process_pch($1)
265
+ when PREPROCESS_MATCHER
266
+ formatter.format_preprocess($1)
267
+ when PBXCP_MATCHER
268
+ formatter.format_pbxcp($1)
269
+ when TESTS_RUN_COMPLETION_MATCHER
270
+ formatter.format_test_run_finished($1, $2)
271
+ when TESTS_RUN_START_MATCHER
272
+ formatter.format_test_run_started($1)
273
+ when TEST_SUITE_START_MATCHER
274
+ formatter.format_test_suite_started($1)
275
+ when TIFFUTIL_MATCHER
276
+ formatter.format_tiffutil($1)
277
+ when TOUCH_MATCHER
278
+ formatter.format_touch($1, $2)
279
+ else
280
+ ""
281
+ end
282
+ end
283
+
284
+ private
285
+
286
+ def update_test_state(text)
287
+ case text
288
+ when TESTS_RUN_START_MATCHER
289
+ @tests_done = false
290
+ @formatted_summary = false
291
+ @failures = {}
292
+ when TESTS_RUN_COMPLETION_MATCHER
293
+ @tests_done = true
294
+ when FAILING_TEST_MATCHER
295
+ store_failure($1, $2, $3, $4)
296
+ end
297
+ end
298
+
299
+ # @ return Hash { :file_name, :file_path, :reason, :line }
300
+ def update_error_state(text)
301
+ if text =~ COMPILE_ERROR_MATCHER
302
+ @formatting_error = true
303
+ current_error[:reason] = $3
304
+ current_error[:file_path] = $1
305
+ current_error[:file_name] = $2
306
+ elsif text =~ CURSOR_MATCHER
307
+ @formatting_error = false
308
+ current_error[:cursor] = $1.chomp
309
+ elsif @formatting_error
310
+ current_error[:line] = text.chomp
311
+ end
312
+ end
313
+
314
+ def update_linker_failure_state(text)
315
+ if text =~ LINKER_UNDEFINED_SYMBOLS_MATCHER ||
316
+ text =~ LINKER_DUPLICATE_SYMBOLS_MATCHER
317
+
318
+ current_linker_failure[:message] = $1
319
+ @formatting_linker_failure = true
320
+ end
321
+ return unless @formatting_linker_failure
322
+
323
+ case text
324
+ when SYMBOL_REFERENCED_FROM_MATCHER
325
+ current_linker_failure[:symbol] = $1
326
+ when LINKER_UNDEFINED_SYMBOL_LOCATION_MATCHER
327
+ current_linker_failure[:reference] = text.strip
328
+ when LINKER_DUPLICATE_SYMBOLS_LOCATION_MATCHER
329
+ current_linker_failure[:files] << $1
330
+ end
331
+ end
332
+
333
+ # TODO: clean up the mess around all this
334
+ def should_format_error?
335
+ current_error[:reason] && current_error[:cursor] && current_error[:line]
336
+ end
337
+
338
+ def should_format_undefined_symbols?
339
+ current_linker_failure[:message] &&
340
+ current_linker_failure[:symbol] &&
341
+ current_linker_failure[:reference]
342
+ end
343
+
344
+ def should_format_duplicate_symbols?
345
+ current_linker_failure[:message] &&
346
+ current_linker_failure[:files].count > 1
347
+ end
348
+
349
+ def current_error
350
+ @current_error ||= {}
351
+ end
352
+
353
+ def current_linker_failure
354
+ @linker_failure ||= { :files => [] }
355
+ end
356
+
357
+ def format_compile_error
358
+ error = current_error.dup
359
+ @current_error = {}
360
+ formatter.format_compile_error(error[:file_name],
361
+ error[:file_path],
362
+ error[:reason],
363
+ error[:line],
364
+ error[:cursor])
365
+ end
366
+
367
+ def format_undefined_symbols
368
+ result = formatter.format_undefined_symbols(
369
+ current_linker_failure[:message],
370
+ current_linker_failure[:symbol],
371
+ current_linker_failure[:reference]
372
+ )
373
+ reset_linker_format_state
374
+ result
375
+ end
376
+
377
+ def format_duplicate_symbols
378
+ result = formatter.format_duplicate_symbols(
379
+ current_linker_failure[:message],
380
+ current_linker_failure[:files]
381
+ )
382
+ reset_linker_format_state
383
+ result
384
+ end
385
+
386
+ def reset_linker_format_state
387
+ @linker_failure = nil
388
+ @formatting_linker_failure = false
389
+ end
390
+
391
+ def store_failure(file, test_suite, test_case, reason)
392
+ failures_per_suite[test_suite] ||= []
393
+ failures_per_suite[test_suite] << {
394
+ :file_path => file,
395
+ :reason => reason,
396
+ :test_case => test_case
397
+ }
398
+ end
399
+
400
+ def failures_per_suite
401
+ @failures ||= {}
402
+ end
403
+
404
+ def format_summary_if_needed(executed_message)
405
+ return "" unless should_format_summary?
406
+
407
+ @formatted_summary = true
408
+ formatter.format_test_summary(executed_message, failures_per_suite)
409
+ end
410
+
411
+ def should_format_summary?
412
+ @tests_done && !@formatted_summary
413
+ end
414
+
415
+ def unescaped(*escaped_values)
416
+ escaped_values.map { |v| v.gsub('\\', '') }
417
+ end
418
+
419
+ end
420
+ end
421
+