xcpretty-bb 0.1.12.bb1

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hound.yml +2 -0
  4. data/.kick +17 -0
  5. data/.rubocop.yml +239 -0
  6. data/.travis.yml +11 -0
  7. data/CHANGELOG.md +200 -0
  8. data/CONTRIBUTING.md +64 -0
  9. data/Gemfile +9 -0
  10. data/LICENSE.txt +61 -0
  11. data/README.md +93 -0
  12. data/Rakefile +26 -0
  13. data/assets/report.html.erb +172 -0
  14. data/bin/xcpretty +85 -0
  15. data/features/assets/RACCommandSpec, line 80, hello xcpretty.png +0 -0
  16. data/features/assets/apple_raw.png +0 -0
  17. data/features/custom_formatter.feature +15 -0
  18. data/features/fixtures/xcodebuild.log +5963 -0
  19. data/features/html_report.feature +54 -0
  20. data/features/json_compilation_database_report.feature +21 -0
  21. data/features/junit_report.feature +44 -0
  22. data/features/knock_format.feature +11 -0
  23. data/features/simple_format.feature +204 -0
  24. data/features/steps/formatting_steps.rb +330 -0
  25. data/features/steps/html_steps.rb +32 -0
  26. data/features/steps/json_steps.rb +37 -0
  27. data/features/steps/junit_steps.rb +39 -0
  28. data/features/steps/report_steps.rb +22 -0
  29. data/features/steps/xcpretty_steps.rb +31 -0
  30. data/features/support/env.rb +117 -0
  31. data/features/tap_format.feature +31 -0
  32. data/features/test_format.feature +49 -0
  33. data/features/xcpretty.feature +14 -0
  34. data/lib/xcpretty/ansi.rb +72 -0
  35. data/lib/xcpretty/formatters/formatter.rb +177 -0
  36. data/lib/xcpretty/formatters/knock.rb +35 -0
  37. data/lib/xcpretty/formatters/rspec.rb +33 -0
  38. data/lib/xcpretty/formatters/simple.rb +200 -0
  39. data/lib/xcpretty/formatters/tap.rb +40 -0
  40. data/lib/xcpretty/parser.rb +591 -0
  41. data/lib/xcpretty/printer.rb +24 -0
  42. data/lib/xcpretty/reporters/html.rb +98 -0
  43. data/lib/xcpretty/reporters/json_compilation_database.rb +62 -0
  44. data/lib/xcpretty/reporters/junit.rb +102 -0
  45. data/lib/xcpretty/snippet.rb +38 -0
  46. data/lib/xcpretty/syntax.rb +51 -0
  47. data/lib/xcpretty/term.rb +14 -0
  48. data/lib/xcpretty/version.rb +4 -0
  49. data/lib/xcpretty.rb +37 -0
  50. data/spec/fixtures/NSStringTests.m +64 -0
  51. data/spec/fixtures/constants.rb +600 -0
  52. data/spec/fixtures/custom_formatter.rb +18 -0
  53. data/spec/fixtures/oneliner.m +1 -0
  54. data/spec/fixtures/raw_kiwi_compilation_fail.txt +24 -0
  55. data/spec/fixtures/raw_kiwi_fail.txt +1896 -0
  56. data/spec/fixtures/raw_specta_fail.txt +3110 -0
  57. data/spec/spec_helper.rb +7 -0
  58. data/spec/support/matchers/colors.rb +21 -0
  59. data/spec/xcpretty/ansi_spec.rb +47 -0
  60. data/spec/xcpretty/formatters/formatter_spec.rb +140 -0
  61. data/spec/xcpretty/formatters/rspec_spec.rb +56 -0
  62. data/spec/xcpretty/formatters/simple_spec.rb +173 -0
  63. data/spec/xcpretty/parser_spec.rb +542 -0
  64. data/spec/xcpretty/printer_spec.rb +55 -0
  65. data/spec/xcpretty/snippet_spec.rb +46 -0
  66. data/spec/xcpretty/syntax_spec.rb +39 -0
  67. data/spec/xcpretty/term_spec.rb +26 -0
  68. data/xcpretty.gemspec +37 -0
  69. metadata +237 -0
@@ -0,0 +1,591 @@
1
+ # encoding: utf-8
2
+
3
+ module XCPretty
4
+
5
+ module Matchers
6
+
7
+ # @regex Captured groups
8
+ # $1 file_path
9
+ # $2 file_name
10
+ ANALYZE_MATCHER = /^Analyze(?:Shallow)?\s(.*\/(.*\.m))*/
11
+
12
+ # @regex Captured groups
13
+ # $1 target
14
+ # $2 project
15
+ # $3 configuration
16
+ BUILD_TARGET_MATCHER = /^=== BUILD TARGET\s(.*)\sOF PROJECT\s(.*)\sWITH.*CONFIGURATION\s(.*)\s===/
17
+
18
+ # @regex Captured groups
19
+ # $1 target
20
+ # $2 project
21
+ # $3 configuration
22
+ ANALYZE_TARGET_MATCHER = /^=== ANALYZE TARGET\s(.*)\sOF PROJECT\s(.*)\sWITH.*CONFIGURATION\s(.*)\s===/
23
+
24
+ # @regex Nothing returned here for now
25
+ CHECK_DEPENDENCIES_MATCHER = /^Check dependencies/
26
+
27
+ # @regex Captured groups
28
+ # $1 command path
29
+ # $2 arguments
30
+ SHELL_COMMAND_MATCHER = /^\s{4}(cd|setenv|(?:[\w\/:\\\s\-.]+?\/)?[\w\-]+)\s(.*)$/
31
+
32
+ # @regex Nothing returned here for now
33
+ CLEAN_REMOVE_MATCHER = /^Clean.Remove/
34
+
35
+ # @regex Captured groups
36
+ # $1 target
37
+ # $2 project
38
+ # $3 configuration
39
+ CLEAN_TARGET_MATCHER = /^=== CLEAN TARGET\s(.*)\sOF PROJECT\s(.*)\sWITH CONFIGURATION\s(.*)\s===/
40
+
41
+ # @regex Captured groups
42
+ # $1 = file
43
+ CODESIGN_MATCHER = /^CodeSign\s((?:\\ |[^ ])*)$/
44
+
45
+ # @regex Captured groups
46
+ # $1 = file
47
+ CODESIGN_FRAMEWORK_MATCHER = /^CodeSign\s((?:\\ |[^ ])*.framework)\/Versions/
48
+
49
+ # @regex Captured groups
50
+ # $1 file_path
51
+ # $2 file_name (e.g. KWNull.m)
52
+ COMPILE_MATCHER = /^Compile[\w]+\s.+?\s((?:\\.|[^ ])+\/((?:\\.|[^ ])+\.(?:m|mm|c|cc|cpp|cxx|swift)))\s.*/
53
+
54
+ # @regex Captured groups
55
+ # $1 compiler_command
56
+ # $2 file_path
57
+ COMPILE_COMMAND_MATCHER = /^\s*(.*\/usr\/bin\/clang\s.*\s\-c\s(.*\.(?:m|mm|c|cc|cpp|cxx))\s.*\.o)$/
58
+
59
+ # @regex Captured groups
60
+ # $1 file_path
61
+ # $2 file_name (e.g. MainMenu.xib)
62
+ COMPILE_XIB_MATCHER = /^CompileXIB\s(.*\/(.*\.xib))/
63
+
64
+ # @regex Captured groups
65
+ # $1 file_path
66
+ # $2 file_name (e.g. Main.storyboard)
67
+ COMPILE_STORYBOARD_MATCHER = /^CompileStoryboard\s(.*\/([^\/].*\.storyboard))/
68
+
69
+ # @regex Captured groups
70
+ # $1 source file
71
+ # $2 target file
72
+ COPY_HEADER_MATCHER = /^CpHeader\s(.*\.h)\s(.*\.h)/
73
+
74
+ # @regex Captured groups
75
+ # $1 source file
76
+ # $2 target file
77
+ COPY_PLIST_MATCHER = /^CopyPlistFile\s(.*\.plist)\s(.*\.plist)/
78
+
79
+ # $1 file
80
+ COPY_STRINGS_MATCHER = /^CopyStringsFile.*\/(.*.strings)/
81
+
82
+ # @regex Captured groups
83
+ # $1 resource
84
+ CPRESOURCE_MATCHER = /^CpResource\s(.*)\s\//
85
+
86
+ # @regex Captured groups
87
+ #
88
+ EXECUTED_MATCHER = /^\s*Executed/
89
+
90
+ # @regex Captured groups
91
+ # $1 = file
92
+ # $2 = test_suite
93
+ # $3 = test_case
94
+ # $4 = reason
95
+ FAILING_TEST_MATCHER = /^\s*(.+:\d+):\serror:\s[\+\-]\[(.*)\s(.*)\]\s:(?:\s'.*'\s\[FAILED\],)?\s(.*)/
96
+
97
+ # @regex Captured groups
98
+ # $1 = dsym
99
+ GENERATE_DSYM_MATCHER = /^GenerateDSYMFile \/.*\/(.*\.dSYM)/
100
+
101
+ # @regex Captured groups
102
+ # $1 = library
103
+ LIBTOOL_MATCHER = /^Libtool.*\/(.*\.a)/
104
+
105
+ # @regex Captured groups
106
+ # $1 = target
107
+ # $2 = build_variants (normal, profile, debug)
108
+ # $3 = architecture
109
+ LINKING_MATCHER = /^Ld \/.*\/(.*) (.*) (.*)$/
110
+
111
+ # @regex Captured groups
112
+ # $1 = suite
113
+ # $2 = test_case
114
+ # $3 = time
115
+ PASSING_TEST_MATCHER = /^\s*Test Case\s'-\[(.*)\s(.*)\]'\spassed\s\((\d*\.\d{3})\sseconds\)/
116
+
117
+ # @regex Captured groups
118
+ # $1 = suite
119
+ # $2 = test_case
120
+ PENDING_TEST_MATCHER = /^Test Case\s'-\[(.*)\s(.*)PENDING\]'\spassed/
121
+
122
+ # @regex Captured groups
123
+ # $1 = suite
124
+ # $2 = test_case
125
+ # $3 = time
126
+ MEASURING_TEST_MATCHER = /^[^:]*:[^:]*:\sTest Case\s'-\[(.*)\s(.*)\]'\smeasured\s\[Time,\sseconds\]\saverage:\s(\d*\.\d{3}),/
127
+
128
+ PHASE_SUCCESS_MATCHER = /^\*\*\s(.*)\sSUCCEEDED\s\*\*/
129
+
130
+ # @regex Captured groups
131
+ # $1 = file
132
+ PROCESS_PCH_MATCHER = /^ProcessPCH\s.*\s(.*.pch)/
133
+
134
+ # @regex Captured groups
135
+ # $1 file_path
136
+ PROCESS_PCH_COMMAND_MATCHER = /^\s*.*\/usr\/bin\/clang\s.*\s\-c\s(.*)\s\-o\s.*/
137
+
138
+ # @regex Captured groups
139
+ # $1 = file
140
+ PREPROCESS_MATCHER = /^Preprocess\s(?:(?:\\ |[^ ])*)\s((?:\\ |[^ ])*)$/
141
+
142
+ # @regex Captured groups
143
+ # $1 = file
144
+ PBXCP_MATCHER = /^PBXCp\s((?:\\ |[^ ])*)/
145
+
146
+ # @regex Captured groups
147
+ # $1 = file
148
+ PROCESS_INFO_PLIST_MATCHER = /^ProcessInfoPlistFile\s.*\.plist\s(.*\/+(.*\.plist))/
149
+
150
+ # @regex Captured groups
151
+ # $1 = suite
152
+ # $2 = time
153
+ TESTS_RUN_COMPLETION_MATCHER = /^\s*Test Suite '(?:.*\/)?(.*[ox]ctest.*)' (finished|passed|failed) at (.*)/
154
+
155
+ # @regex Captured groups
156
+ # $1 = suite
157
+ # $2 = time
158
+ TESTS_RUN_START_MATCHER = /^\s*Test Suite '(?:.*\/)?(.*[ox]ctest.*)' started at(.*)/
159
+
160
+ # @regex Captured groups
161
+ # $1 test suite name
162
+ TEST_SUITE_START_MATCHER = /^\s*Test Suite '(.*)' started at/
163
+
164
+ # @regex Captured groups
165
+ # $1 file_name
166
+ TIFFUTIL_MATCHER = /^TiffUtil\s(.*)/
167
+
168
+ # @regex Captured groups
169
+ # $1 file_path
170
+ # $2 file_name
171
+ TOUCH_MATCHER = /^Touch\s(.*\/(.+))/
172
+
173
+ # @regex Captured groups
174
+ # $1 file_path
175
+ WRITE_FILE_MATCHER = /^write-file\s(.*)/
176
+
177
+ # @regex Captured groups
178
+ WRITE_AUXILIARY_FILES = /^Write auxiliary files/
179
+
180
+ module PhaseScript
181
+ # @regex Captured groups
182
+ # $1 = phase_details
183
+ PHASE_SCRIPT_EXECUTION_MATCHER = /^PhaseScriptExecution\s(.*)\s/
184
+
185
+ # @regex Captured groups
186
+ # $1 = file not found
187
+ PHASE_SCRIPT_NO_SUCH_FILE_OR_DIRECTORY_MATCHER = /: (.*): (No such file or directory)$/
188
+
189
+ # @regex Captured groups
190
+ # $1 = command
191
+ # $2 = exit code
192
+ PHASE_SCRIPT_COMMAND_FAILED_MATCHER = /^Command (.*) failed with exit code (\d+)/
193
+ end
194
+
195
+ module Warnings
196
+ # $1 = file_path
197
+ # $2 = file_name
198
+ # $3 = reason
199
+ COMPILE_WARNING_MATCHER = /^(\/.+\/(.*):.*:.*):\swarning:\s(.*)$/
200
+
201
+ # $1 = ld prefix
202
+ # $2 = warning message
203
+ LD_WARNING_MATCHER = /^(ld: )warning: (.*)/
204
+
205
+ # @regex Captured groups
206
+ # $1 = whole warning
207
+ GENERIC_WARNING_MATCHER = /^warning:\s(.*)$/
208
+ end
209
+
210
+ module Errors
211
+ # @regex Captured groups
212
+ # $1 = whole error
213
+ CLANG_ERROR_MATCHER = /^(clang: error:.*)$/
214
+
215
+ # @regex Captured groups
216
+ # $1 = whole error
217
+ CODESIGN_ERROR_MATCHER = /^(Code\s?Sign error:.*)$/
218
+
219
+ # @regex Captured groups
220
+ # $1 = file_path
221
+ # $2 = file_name
222
+ # $3 = reason
223
+ COMPILE_ERROR_MATCHER = /^(\/.+\/(.*):.*:.*):\s(?:fatal\s)?error:\s(.*)$/
224
+
225
+ # @regex Captured groups
226
+ # $1 cursor (with whitespaces and tildes)
227
+ CURSOR_MATCHER = /^([\s~]*\^[\s~]*)$/
228
+
229
+ # @regex Captured groups
230
+ # $1 = whole error.
231
+ # it varies a lot, not sure if it makes sense to catch everything separately
232
+ FATAL_ERROR_MATCHER = /^(fatal error:.*)$/
233
+
234
+ # @regex Captured groups
235
+ # $1 = whole error.
236
+ # $2 = file path
237
+ FILE_MISSING_ERROR_MATCHER = /^<unknown>:0:\s(error:\s.*)\s'(\/.+\/.*\..*)'$/
238
+
239
+ # $1 = whole error
240
+ LD_ERROR_MATCHER = /^(ld:.*)/
241
+
242
+ # @regex Captured groups
243
+ # $1 file path
244
+ LINKER_DUPLICATE_SYMBOLS_LOCATION_MATCHER = /^\s+(\/.*\.o[\)]?)$/
245
+
246
+ # @regex Captured groups
247
+ # $1 reason
248
+ LINKER_DUPLICATE_SYMBOLS_MATCHER = /^(duplicate symbol .*):$/
249
+
250
+ # @regex Captured groups
251
+ # $1 symbol location
252
+ LINKER_UNDEFINED_SYMBOL_LOCATION_MATCHER = /^(.* in .*\.o)$/
253
+
254
+ # @regex Captured groups
255
+ # $1 reason
256
+ LINKER_UNDEFINED_SYMBOLS_MATCHER = /^(Undefined symbols for architecture .*):$/
257
+
258
+ # @regex Captured groups
259
+ # $1 reason
260
+ PODS_ERROR_MATCHER = /^(error:\s.*)/
261
+
262
+ # @regex Captured groups
263
+ # $1 = reference
264
+ SYMBOL_REFERENCED_FROM_MATCHER = /\s+"(.*)", referenced from:$/
265
+ end
266
+ end
267
+
268
+ class Parser
269
+
270
+ include Matchers
271
+ include Matchers::PhaseScript
272
+ include Matchers::Errors
273
+ include Matchers::Warnings
274
+
275
+ attr_reader :formatter
276
+
277
+ def initialize(formatter)
278
+ @formatter = formatter
279
+ end
280
+
281
+ def parse(text)
282
+ update_test_state(text)
283
+ update_error_state(text)
284
+ update_linker_failure_state(text)
285
+ update_phase_script_state(text)
286
+
287
+ return format_compile_error if should_format_error?
288
+ return format_compile_warning if should_format_warning?
289
+ return format_undefined_symbols if should_format_undefined_symbols?
290
+ return format_duplicate_symbols if should_format_duplicate_symbols?
291
+ return format_phase_script_error if should_format_phase_script_error?
292
+
293
+ case text
294
+ when ANALYZE_MATCHER
295
+ formatter.format_analyze($2, $1)
296
+ when BUILD_TARGET_MATCHER
297
+ formatter.format_build_target($1, $2, $3)
298
+ when ANALYZE_TARGET_MATCHER
299
+ formatter.format_analyze_target($1, $2, $3)
300
+ when CLEAN_REMOVE_MATCHER
301
+ formatter.format_clean_remove
302
+ when CLEAN_TARGET_MATCHER
303
+ formatter.format_clean_target($1, $2, $3)
304
+ when COPY_STRINGS_MATCHER
305
+ formatter.format_copy_strings_file($1)
306
+ when CHECK_DEPENDENCIES_MATCHER
307
+ formatter.format_check_dependencies
308
+ when CLANG_ERROR_MATCHER
309
+ formatter.format_error($1)
310
+ when CODESIGN_FRAMEWORK_MATCHER
311
+ formatter.format_codesign($1)
312
+ when CODESIGN_MATCHER
313
+ formatter.format_codesign($1)
314
+ when CODESIGN_ERROR_MATCHER
315
+ formatter.format_error($1)
316
+ when COMPILE_MATCHER
317
+ formatter.format_compile($2, $1)
318
+ when COMPILE_COMMAND_MATCHER
319
+ formatter.format_compile_command($1, $2)
320
+ when COMPILE_XIB_MATCHER
321
+ formatter.format_compile_xib($2, $1)
322
+ when COMPILE_STORYBOARD_MATCHER
323
+ formatter.format_compile_storyboard($2, $1)
324
+ when COPY_HEADER_MATCHER
325
+ formatter.format_copy_header_file($1, $2)
326
+ when COPY_PLIST_MATCHER
327
+ formatter.format_copy_plist_file($1, $2)
328
+ when CPRESOURCE_MATCHER
329
+ formatter.format_cpresource($1)
330
+ when EXECUTED_MATCHER
331
+ format_summary_if_needed(text)
332
+ when FAILING_TEST_MATCHER
333
+ formatter.format_failing_test($2, $3, $4, $1)
334
+ when FATAL_ERROR_MATCHER
335
+ formatter.format_error($1)
336
+ when FILE_MISSING_ERROR_MATCHER
337
+ formatter.format_file_missing_error($1, $2)
338
+ when GENERATE_DSYM_MATCHER
339
+ formatter.format_generate_dsym($1)
340
+ when LD_WARNING_MATCHER
341
+ formatter.format_ld_warning($1 + $2)
342
+ when LD_ERROR_MATCHER
343
+ formatter.format_error($1)
344
+ when LIBTOOL_MATCHER
345
+ formatter.format_libtool($1)
346
+ when LINKING_MATCHER
347
+ formatter.format_linking($1, $2, $3)
348
+ when MEASURING_TEST_MATCHER
349
+ formatter.format_measuring_test($1, $2, $3)
350
+ when PENDING_TEST_MATCHER
351
+ formatter.format_pending_test($1, $2)
352
+ when PASSING_TEST_MATCHER
353
+ formatter.format_passing_test($1, $2, $3)
354
+ when PODS_ERROR_MATCHER
355
+ formatter.format_error($1)
356
+ when PROCESS_INFO_PLIST_MATCHER
357
+ formatter.format_process_info_plist(*unescaped($2, $1))
358
+ when PHASE_SCRIPT_EXECUTION_MATCHER
359
+ formatter.format_phase_script_execution(*unescaped($1))
360
+ when PHASE_SUCCESS_MATCHER
361
+ formatter.format_phase_success($1)
362
+ when PROCESS_PCH_MATCHER
363
+ formatter.format_process_pch($1)
364
+ when PROCESS_PCH_COMMAND_MATCHER
365
+ formatter.format_process_pch_command($1)
366
+ when PREPROCESS_MATCHER
367
+ formatter.format_preprocess($1)
368
+ when PBXCP_MATCHER
369
+ formatter.format_pbxcp($1)
370
+ when TESTS_RUN_COMPLETION_MATCHER
371
+ formatter.format_test_run_finished($1, $3)
372
+ when TESTS_RUN_START_MATCHER
373
+ formatter.format_test_run_started($1)
374
+ when TEST_SUITE_START_MATCHER
375
+ formatter.format_test_suite_started($1)
376
+ when TIFFUTIL_MATCHER
377
+ formatter.format_tiffutil($1)
378
+ when TOUCH_MATCHER
379
+ formatter.format_touch($1, $2)
380
+ when WRITE_FILE_MATCHER
381
+ formatter.format_write_file($1)
382
+ when WRITE_AUXILIARY_FILES
383
+ formatter.format_write_auxiliary_files
384
+ when SHELL_COMMAND_MATCHER
385
+ formatter.format_shell_command($1, $2)
386
+ when GENERIC_WARNING_MATCHER
387
+ formatter.format_warning($1)
388
+ else
389
+ ""
390
+ end
391
+ end
392
+
393
+ private
394
+
395
+ def update_phase_script_state(text)
396
+ case text
397
+ when PHASE_SCRIPT_EXECUTION_MATCHER
398
+ @current_phase_script_text = text
399
+ when PHASE_SCRIPT_COMMAND_FAILED_MATCHER, PHASE_SCRIPT_NO_SUCH_FILE_OR_DIRECTORY_MATCHER
400
+ unless @current_phase_script_text.nil?
401
+ @current_phase_script_text += text
402
+ current_phase_script_failure[:text] = @current_phase_script_text
403
+ current_phase_script_failure[:error] = text
404
+ end
405
+ else
406
+ unless @current_phase_script_text.nil?
407
+ @current_phase_script_text += text
408
+ end
409
+ end
410
+ end
411
+
412
+ def update_test_state(text)
413
+ case text
414
+ when TESTS_RUN_START_MATCHER
415
+ @tests_done = false
416
+ @formatted_summary = false
417
+ @failures = {}
418
+ when TESTS_RUN_COMPLETION_MATCHER
419
+ @tests_done = true
420
+ when FAILING_TEST_MATCHER
421
+ store_failure($1, $2, $3, $4)
422
+ end
423
+ end
424
+
425
+ # @ return Hash { :file_name, :file_path, :reason, :line }
426
+ def update_error_state(text)
427
+ update_error = lambda {
428
+ current_issue[:reason] = $3
429
+ current_issue[:file_path] = $1
430
+ current_issue[:file_name] = $2
431
+ }
432
+ if text =~ COMPILE_ERROR_MATCHER
433
+ @formatting_error = true
434
+ update_error.call
435
+ elsif text =~ COMPILE_WARNING_MATCHER
436
+ @formatting_warning = true
437
+ update_error.call
438
+ elsif text =~ CURSOR_MATCHER
439
+ current_issue[:cursor] = $1.chomp
440
+ elsif @formatting_error || @formatting_warning
441
+ current_issue[:line] = text.chomp
442
+ end
443
+ end
444
+
445
+ def update_linker_failure_state(text)
446
+ if text =~ LINKER_UNDEFINED_SYMBOLS_MATCHER ||
447
+ text =~ LINKER_DUPLICATE_SYMBOLS_MATCHER
448
+
449
+ current_linker_failure[:message] = $1
450
+ @formatting_linker_failure = true
451
+ end
452
+ return unless @formatting_linker_failure
453
+
454
+ case text
455
+ when SYMBOL_REFERENCED_FROM_MATCHER
456
+ current_linker_failure[:symbol] = $1
457
+ when LINKER_UNDEFINED_SYMBOL_LOCATION_MATCHER
458
+ current_linker_failure[:reference] = text.strip
459
+ when LINKER_DUPLICATE_SYMBOLS_LOCATION_MATCHER
460
+ current_linker_failure[:files] << $1
461
+ end
462
+ end
463
+
464
+ # TODO: clean up the mess around all this
465
+ def should_format_error?
466
+ @formatting_error && error_or_warning_is_present
467
+ end
468
+
469
+ def should_format_warning?
470
+ @formatting_warning && error_or_warning_is_present
471
+ end
472
+
473
+ def error_or_warning_is_present
474
+ current_issue[:reason] && current_issue[:cursor] && current_issue[:line]
475
+ end
476
+
477
+ def should_format_undefined_symbols?
478
+ current_linker_failure[:message] && current_linker_failure[:symbol] && current_linker_failure[:reference]
479
+ end
480
+
481
+ def should_format_duplicate_symbols?
482
+ current_linker_failure[:message] && current_linker_failure[:files].count > 1
483
+ end
484
+
485
+ def should_format_phase_script_error?
486
+ current_phase_script_failure[:error] && current_phase_script_failure[:text]
487
+ end
488
+
489
+ def current_issue
490
+ @current_issue ||= {}
491
+ end
492
+
493
+ def current_linker_failure
494
+ @linker_failure ||= {files: []}
495
+ end
496
+
497
+ def current_phase_script_failure
498
+ @phase_script_failure ||= {}
499
+ end
500
+
501
+ def format_compile_error
502
+ error = current_issue.dup
503
+ @current_issue = {}
504
+ @formatting_error = false
505
+ formatter.format_compile_error(error[:file_name],
506
+ error[:file_path],
507
+ error[:reason],
508
+ error[:line],
509
+ error[:cursor])
510
+ end
511
+
512
+ def format_compile_warning
513
+ warning = current_issue.dup
514
+ @current_issue = {}
515
+ @formatting_warning = false
516
+ formatter.format_compile_warning(warning[:file_name],
517
+ warning[:file_path],
518
+ warning[:reason],
519
+ warning[:line],
520
+ warning[:cursor])
521
+ end
522
+
523
+ def format_undefined_symbols
524
+ result = formatter.format_undefined_symbols(
525
+ current_linker_failure[:message],
526
+ current_linker_failure[:symbol],
527
+ current_linker_failure[:reference]
528
+ )
529
+ reset_linker_format_state
530
+ result
531
+ end
532
+
533
+ def format_duplicate_symbols
534
+ result = formatter.format_duplicate_symbols(
535
+ current_linker_failure[:message],
536
+ current_linker_failure[:files]
537
+ )
538
+ reset_linker_format_state
539
+ result
540
+ end
541
+
542
+ def format_phase_script_error
543
+ result = formatter.format_phase_script_error(current_phase_script_failure[:error],
544
+ current_phase_script_failure[:text]
545
+ )
546
+
547
+ reset_phase_script_state
548
+ result
549
+ end
550
+
551
+ def reset_phase_script_state
552
+ @phase_script_failure = nil
553
+ @current_phase_script_text = nil
554
+ end
555
+
556
+ def reset_linker_format_state
557
+ @linker_failure = nil
558
+ @formatting_linker_failure = false
559
+ end
560
+
561
+ def store_failure(file, test_suite, test_case, reason)
562
+ failures_per_suite[test_suite] ||= []
563
+ failures_per_suite[test_suite] << {
564
+ file_path: file,
565
+ reason: reason,
566
+ test_case: test_case
567
+ }
568
+ end
569
+
570
+ def failures_per_suite
571
+ @failures ||= {}
572
+ end
573
+
574
+ def format_summary_if_needed(executed_message)
575
+ return "" unless should_format_summary?
576
+
577
+ @formatted_summary = true
578
+ formatter.format_test_summary(executed_message, failures_per_suite)
579
+ end
580
+
581
+ def should_format_summary?
582
+ @tests_done && !@formatted_summary
583
+ end
584
+
585
+ def unescaped(*escaped_values)
586
+ escaped_values.map { |v| v.delete('\\') }
587
+ end
588
+
589
+ end
590
+ end
591
+
@@ -0,0 +1,24 @@
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
+ unless formatted_text.empty?
17
+ STDOUT.print(formatted_text + formatter.optional_newline)
18
+ STDOUT.flush
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,98 @@
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
+ SCREENSHOT_DIR = 'build/reports'
8
+
9
+ def load_dependencies
10
+ unless @@loaded ||= false
11
+ require 'fileutils'
12
+ require 'pathname'
13
+ require 'erb'
14
+ @@loaded = true
15
+ end
16
+ end
17
+
18
+ def initialize(options)
19
+ load_dependencies
20
+ @test_suites = {}
21
+ @filepath = options[:path] || FILEPATH
22
+ @parser = Parser.new(self)
23
+ @test_count = 0
24
+ @fail_count = 0
25
+ @collect_screenshots = options[:screenshots]
26
+ end
27
+
28
+ def handle(line)
29
+ @parser.parse(line)
30
+ end
31
+
32
+ def format_failing_test(suite, test_case, reason, file)
33
+ add_test(suite, name: test_case, failing: true,
34
+ reason: reason, file: file,
35
+ snippet: formatted_snippet(file))
36
+ end
37
+
38
+ def format_passing_test(suite, test_case, time)
39
+ add_test(suite, name: test_case, time: time)
40
+ end
41
+
42
+ def finish
43
+ FileUtils.mkdir_p(File.dirname(@filepath))
44
+ write_report
45
+ end
46
+
47
+ private
48
+
49
+ def formatted_snippet(filepath)
50
+ snippet = Snippet.from_filepath(filepath)
51
+ Syntax.highlight(snippet)
52
+ end
53
+
54
+
55
+ def add_test(suite_name, data)
56
+ @test_count += 1
57
+ @test_suites[suite_name] ||= {tests: [], screenshots: []}
58
+ @test_suites[suite_name][:tests] << data
59
+ if data[:failing]
60
+ @test_suites[suite_name][:failing] = true
61
+ @fail_count += 1
62
+ end
63
+ end
64
+
65
+ def write_report
66
+ if @collect_screenshots
67
+ load_screenshots
68
+ end
69
+ File.open(@filepath, 'w') do |f|
70
+ # WAT: get rid of these locals. BTW Cucumber fails if you remove them
71
+ test_suites = @test_suites
72
+ fail_count = @fail_count
73
+ test_count = @test_count
74
+ erb = ERB.new(File.open(TEMPLATE, 'r').read)
75
+ f.write erb.result(binding)
76
+ end
77
+ end
78
+
79
+ def load_screenshots
80
+ Dir.foreach(SCREENSHOT_DIR) do |item|
81
+ next if item == '.' || item == '..' || File.extname(item) != '.png'
82
+
83
+ suite_name = find_test_suite(item)
84
+ next if suite_name.nil?
85
+
86
+ @test_suites[suite_name][:screenshots] << item
87
+ end
88
+ end
89
+
90
+ def find_test_suite(image_name)
91
+ @test_suites.each do |key, value|
92
+ return key if image_name.start_with?(key)
93
+ end
94
+ nil
95
+ end
96
+ end
97
+ end
98
+