xcode-result-bundle-processor 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +23 -0
- data/.csslintrc +34 -0
- data/.eslintrc +253 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1171 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +83 -0
- data/LICENSE +36 -0
- data/README.md +72 -0
- data/Rakefile +50 -0
- data/bin/xcode-result-bundle-processor +37 -0
- data/lib/xcoderesultbundleprocessor.rb +28 -0
- data/lib/xcoderesultbundleprocessor/activity_log_formatter.rb +20 -0
- data/lib/xcoderesultbundleprocessor/element_snapshot.rb +81 -0
- data/lib/xcoderesultbundleprocessor/indented_string_buffer.rb +33 -0
- data/lib/xcoderesultbundleprocessor/keyword_struct.rb +10 -0
- data/lib/xcoderesultbundleprocessor/log_deserializer.rb +26 -0
- data/lib/xcoderesultbundleprocessor/results_bundle.rb +45 -0
- data/lib/xcoderesultbundleprocessor/slf0/class_name_resolver.rb +26 -0
- data/lib/xcoderesultbundleprocessor/slf0/deserializer.rb +45 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/dvtdocumentlocation.rb +16 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/dvttextdocumentlocation.rb +23 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogmessage.rb +26 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogsection.rb +29 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideactivitylogunittestsection.rb +24 -0
- data/lib/xcoderesultbundleprocessor/slf0/model/ideconsoleitem.rb +16 -0
- data/lib/xcoderesultbundleprocessor/slf0/tokenizer.rb +82 -0
- data/lib/xcoderesultbundleprocessor/snapshot_summary.rb +47 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/html_report.rb +156 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/test_summaries.rb +84 -0
- data/lib/xcoderesultbundleprocessor/test_summaries/text_report.rb +40 -0
- data/lib/xcoderesultbundleprocessor/version.rb +3 -0
- data/static/report.css +30 -0
- data/static/report.js +14 -0
- data/xcode-result-bundle-processor.gemspec +30 -0
- metadata +225 -0
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
xcode-result-bundle-processor
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
xcode-result-bundle-processor (1.0.0)
|
5
|
+
CFPropertyList (~> 2.2)
|
6
|
+
awesome_print (~> 1.6)
|
7
|
+
markaby (~> 0.8)
|
8
|
+
methadone (~> 1.9.2)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
CFPropertyList (2.3.2)
|
14
|
+
aruba (0.10.2)
|
15
|
+
childprocess (~> 0.5.6)
|
16
|
+
contracts (~> 0.9)
|
17
|
+
cucumber (>= 1.3.19)
|
18
|
+
ffi (~> 1.9.10)
|
19
|
+
rspec-expectations (>= 2.99)
|
20
|
+
thor (~> 0.19)
|
21
|
+
awesome_print (1.6.1)
|
22
|
+
builder (3.2.2)
|
23
|
+
childprocess (0.5.7)
|
24
|
+
ffi (~> 1.0, >= 1.0.11)
|
25
|
+
codeclimate-test-reporter (0.4.8)
|
26
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
27
|
+
contracts (0.12.0)
|
28
|
+
cucumber (2.1.0)
|
29
|
+
builder (>= 2.1.2)
|
30
|
+
cucumber-core (~> 1.3.0)
|
31
|
+
diff-lcs (>= 1.1.3)
|
32
|
+
gherkin3 (~> 3.1.0)
|
33
|
+
multi_json (>= 1.7.5, < 2.0)
|
34
|
+
multi_test (>= 0.1.2)
|
35
|
+
cucumber-core (1.3.0)
|
36
|
+
gherkin3 (~> 3.1.0)
|
37
|
+
diff-lcs (1.2.5)
|
38
|
+
docile (1.1.5)
|
39
|
+
ffi (1.9.10)
|
40
|
+
gherkin3 (3.1.2)
|
41
|
+
json (1.8.3)
|
42
|
+
markaby (0.8.0)
|
43
|
+
builder
|
44
|
+
methadone (1.9.2)
|
45
|
+
bundler
|
46
|
+
multi_json (1.11.2)
|
47
|
+
multi_test (0.1.2)
|
48
|
+
rake (10.4.2)
|
49
|
+
rdoc (4.2.0)
|
50
|
+
rspec (3.3.0)
|
51
|
+
rspec-core (~> 3.3.0)
|
52
|
+
rspec-expectations (~> 3.3.0)
|
53
|
+
rspec-mocks (~> 3.3.0)
|
54
|
+
rspec-core (3.3.2)
|
55
|
+
rspec-support (~> 3.3.0)
|
56
|
+
rspec-expectations (3.3.1)
|
57
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
58
|
+
rspec-support (~> 3.3.0)
|
59
|
+
rspec-mocks (3.3.2)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.3.0)
|
62
|
+
rspec-support (3.3.0)
|
63
|
+
simplecov (0.10.0)
|
64
|
+
docile (~> 1.1.0)
|
65
|
+
json (~> 1.8)
|
66
|
+
simplecov-html (~> 0.10.0)
|
67
|
+
simplecov-html (0.10.0)
|
68
|
+
thor (0.19.1)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
aruba (~> 0.10)
|
75
|
+
bundler (~> 1.6)
|
76
|
+
codeclimate-test-reporter (~> 0.4)
|
77
|
+
rake (~> 10.4)
|
78
|
+
rdoc (~> 4.2)
|
79
|
+
rspec (~> 3.3)
|
80
|
+
xcode-result-bundle-processor!
|
81
|
+
|
82
|
+
BUNDLED WITH
|
83
|
+
1.10.6
|
data/LICENSE
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyfree Open Innovation License
|
2
|
+
|
3
|
+
This is version 0.4 of the Copyfree Open Innovation License.
|
4
|
+
|
5
|
+
## Terms and Conditions
|
6
|
+
|
7
|
+
Redistributions, modified or unmodified, in whole or in part, must retain
|
8
|
+
applicable copyright or other legal privilege notices, these conditions, and
|
9
|
+
the following license terms and disclaimer. Subject to these conditions, the
|
10
|
+
holder(s) of copyright or other legal privileges, author(s) or assembler(s),
|
11
|
+
and contributors of this work hereby grant to any person who obtains a copy of
|
12
|
+
this work in any form:
|
13
|
+
|
14
|
+
1. Permission to reproduce, modify, distribute, publish, sell, sublicense, use,
|
15
|
+
and/or otherwise deal in the licensed material without restriction.
|
16
|
+
|
17
|
+
2. A perpetual, worldwide, non-exclusive, royalty-free, irrevocable patent
|
18
|
+
license to reproduce, modify, distribute, publish, sell, use, and/or otherwise
|
19
|
+
deal in the licensed material without restriction, for any and all patents:
|
20
|
+
|
21
|
+
a. Held presently or in the future by each such holder of copyright or
|
22
|
+
other legal privilege, author or assembler, or contributor, necessarily
|
23
|
+
infringed by the contributions alone or by combination with the work, of
|
24
|
+
that privilege holder, author or assembler, or contributor.
|
25
|
+
|
26
|
+
b. Necessarily infringed by the work at the time that holder of copyright
|
27
|
+
or other privilege, author or assembler, or contributor made any
|
28
|
+
contribution to the work.
|
29
|
+
|
30
|
+
NO WARRANTY OF ANY KIND IS IMPLIED BY, OR SHOULD BE INFERRED FROM, THIS LICENSE
|
31
|
+
OR THE ACT OF DISTRIBUTION UNDER THE TERMS OF THIS LICENSE, INCLUDING BUT NOT
|
32
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
33
|
+
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS, ASSEMBLERS, OR HOLDERS OF
|
34
|
+
COPYRIGHT OR OTHER LEGAL PRIVILEGE BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
35
|
+
LIABILITY, WHETHER IN ACTION OF CONTRACT, TORT, OR OTHERWISE ARISING FROM, OUT
|
36
|
+
OF, OR IN CONNECTION WITH THE WORK OR THE USE OF OR OTHER DEALINGS IN THE WORK.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/americanexpress/xcode-result-bundle-processor.svg?branch=master)](https://travis-ci.org/americanexpress/xcode-result-bundle-processor)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor/badges/gpa.svg)](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor)
|
3
|
+
[![Test Coverage](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor/badges/coverage.svg)](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor/coverage)
|
4
|
+
[![Issue Count](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor/badges/issue_count.svg)](https://codeclimate.com/github/americanexpress/xcode-result-bundle-processor)
|
5
|
+
|
6
|
+
# XcodeResultBundleProcessor
|
7
|
+
|
8
|
+
This tool converts into Xcode 7's machine-readable results bundle into a human-readable HTML report including
|
9
|
+
detailed activity logs and screenshots for UI tests.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'xcode-result-bundle-processor'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install xcode-result-bundle-processor
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
First, run your UI tests using an invocation like this:
|
28
|
+
|
29
|
+
xcodebuild -workspace MyWorkspace.xcworkspace \
|
30
|
+
-scheme 'My UI Tests' \
|
31
|
+
-sdk iphonesimulator \
|
32
|
+
-resultBundlePath "my_results_bundle" \
|
33
|
+
|
34
|
+
This will put a bunch of mystery files into `my_results_bundle`, which you can transform into un-mystery files
|
35
|
+
via
|
36
|
+
|
37
|
+
bundle exec xcode-result-bundle-processor --save-html-report report my_results_bundle
|
38
|
+
|
39
|
+
The report will appear in `report/index.html`
|
40
|
+
|
41
|
+
## Development
|
42
|
+
|
43
|
+
If you have RVM and change to the working directory, you'll be prompted to install the needed Ruby version if you
|
44
|
+
don't already have it. RVM will also handle creating a gemset.
|
45
|
+
|
46
|
+
To update the dependencies, run
|
47
|
+
|
48
|
+
bundle
|
49
|
+
|
50
|
+
To run tests, run
|
51
|
+
|
52
|
+
bundle exec rake
|
53
|
+
|
54
|
+
To build the gem, fun
|
55
|
+
|
56
|
+
bundle exec rake build
|
57
|
+
|
58
|
+
To execute the tool, run
|
59
|
+
|
60
|
+
bundle exec xcode-result-bundle-processor
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
Contributing is easy and fun! Some guidelines:
|
65
|
+
|
66
|
+
* If you're making a non-trivial change, consider reaching out to Manuel the Maintainer (mwudka@me.com) for an
|
67
|
+
architecture/implementation chat.
|
68
|
+
* Create an issue to track your work and describe your goals
|
69
|
+
* Branch from master and make your changes in the branch
|
70
|
+
* Add tests for any new functionality
|
71
|
+
* When you're happy with your changes and the builds pass, open a PR
|
72
|
+
* If no one has addressed your PR after a day or two, prod Manuel the Maintainer (mwudka@me.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
def dump_load_path
|
2
|
+
puts $LOAD_PATH.join("\n")
|
3
|
+
found = nil
|
4
|
+
$LOAD_PATH.each do |path|
|
5
|
+
if File.exist?(File.join(path,"rspec"))
|
6
|
+
puts "Found rspec in #{path}"
|
7
|
+
if File.exist?(File.join(path,"rspec","core"))
|
8
|
+
puts "Found core"
|
9
|
+
if File.exist?(File.join(path,"rspec","core","rake_task"))
|
10
|
+
puts "Found rake_task"
|
11
|
+
found = path
|
12
|
+
else
|
13
|
+
puts "!! no rake_task"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "!!! no core"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if found.nil?
|
21
|
+
puts "Didn't find rspec/core/rake_task anywhere"
|
22
|
+
else
|
23
|
+
puts "Found in #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'bundler'
|
27
|
+
require 'rake/clean'
|
28
|
+
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
|
31
|
+
require 'cucumber'
|
32
|
+
require 'cucumber/rake/task'
|
33
|
+
gem 'rdoc' # we need the installed RDoc gem, not the system one
|
34
|
+
require 'rdoc/task'
|
35
|
+
|
36
|
+
include Rake::DSL
|
37
|
+
|
38
|
+
Bundler::GemHelper.install_tasks
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new(:spec)
|
41
|
+
|
42
|
+
CUKE_RESULTS = 'results.html'
|
43
|
+
CLEAN << CUKE_RESULTS
|
44
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
45
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
|
46
|
+
t.fork = false
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => [:spec, :features]
|
50
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'xcoderesultbundleprocessor'
|
4
|
+
|
5
|
+
class App
|
6
|
+
include Methadone::Main
|
7
|
+
include Methadone::CLILogging
|
8
|
+
|
9
|
+
main do |results_bundle_path|
|
10
|
+
change_logger(Methadone::CLILogger.new(STDERR, STDERR))
|
11
|
+
|
12
|
+
info "Preparing report for results bundle path at #{results_bundle_path}"
|
13
|
+
|
14
|
+
results_bundle = if File.directory?(results_bundle_path)
|
15
|
+
XcodeResultBundleProcessor::DirectoryResultsBundle.new(results_bundle_path)
|
16
|
+
else
|
17
|
+
XcodeResultBundleProcessor::TarballResultsBundle.new(results_bundle_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
unless options['save-html-report'].nil?
|
21
|
+
XcodeResultBundleProcessor::TestSummaries::HTMLReport.new(results_bundle).save(options['save-html-report'])
|
22
|
+
end
|
23
|
+
|
24
|
+
debug XcodeResultBundleProcessor::TestSummaries::TextReport.format(results_bundle)
|
25
|
+
info 'Success!'
|
26
|
+
end
|
27
|
+
|
28
|
+
description 'Creates friendly report from Xcode results bundle'
|
29
|
+
arg :results_bundle_path
|
30
|
+
on('--save-html-report [report output dir]', 'Save a test result HTML report to given directory')
|
31
|
+
|
32
|
+
version XcodeResultBundleProcessor::VERSION
|
33
|
+
|
34
|
+
use_log_level_option :toggle_debug_on_signal => 'USR1'
|
35
|
+
|
36
|
+
go!
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'cfpropertylist'
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'methadone'
|
5
|
+
require 'markaby'
|
6
|
+
require 'optparse'
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
require 'xcoderesultbundleprocessor/version'
|
10
|
+
require 'xcoderesultbundleprocessor/keyword_struct'
|
11
|
+
require 'xcoderesultbundleprocessor/indented_string_buffer'
|
12
|
+
require 'xcoderesultbundleprocessor/slf0/tokenizer'
|
13
|
+
require 'xcoderesultbundleprocessor/slf0/class_name_resolver'
|
14
|
+
require 'xcoderesultbundleprocessor/slf0/deserializer'
|
15
|
+
require 'xcoderesultbundleprocessor/slf0/model/dvtdocumentlocation'
|
16
|
+
require 'xcoderesultbundleprocessor/slf0/model/dvttextdocumentlocation'
|
17
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogmessage'
|
18
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogsection'
|
19
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideactivitylogunittestsection'
|
20
|
+
require 'xcoderesultbundleprocessor/slf0/model/ideconsoleitem'
|
21
|
+
require 'xcoderesultbundleprocessor/activity_log_formatter'
|
22
|
+
require 'xcoderesultbundleprocessor/log_deserializer'
|
23
|
+
require 'xcoderesultbundleprocessor/test_summaries/test_summaries'
|
24
|
+
require 'xcoderesultbundleprocessor/test_summaries/html_report'
|
25
|
+
require 'xcoderesultbundleprocessor/test_summaries/text_report'
|
26
|
+
require 'xcoderesultbundleprocessor/results_bundle'
|
27
|
+
require 'xcoderesultbundleprocessor/element_snapshot'
|
28
|
+
require 'xcoderesultbundleprocessor/snapshot_summary'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
module ActivityLogFormatter
|
3
|
+
include Methadone::CLILogging
|
4
|
+
|
5
|
+
def self.format(section)
|
6
|
+
debug "Formatting activity log section #{section.ai}"
|
7
|
+
|
8
|
+
return "\n" if section.nil?
|
9
|
+
|
10
|
+
buffer = IndentedStringBuffer.new
|
11
|
+
|
12
|
+
# The top-level section contains the complete log for all test runs, so grabbing that text is enough to
|
13
|
+
# get the full log
|
14
|
+
section = section.subsections.first
|
15
|
+
|
16
|
+
buffer << section.title << section.text.each_line
|
17
|
+
buffer.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
class ElementSnapshot < OpenStruct
|
3
|
+
def initialize(plist)
|
4
|
+
super()
|
5
|
+
|
6
|
+
@objects = plist.value.value['$objects'].value
|
7
|
+
|
8
|
+
root_object = plist.value.value['$top'].value['root']
|
9
|
+
|
10
|
+
self._deserialize_object(root_object).each do |key, value|
|
11
|
+
self[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def _deserialize_dictionary(cfdictionary)
|
16
|
+
if cfdictionary.value.key?('NS.rectval')
|
17
|
+
return self._deserialize_object(cfdictionary.value['NS.rectval'])
|
18
|
+
end
|
19
|
+
|
20
|
+
if cfdictionary.value.key?('$classname')
|
21
|
+
return cfdictionary.value['$classname'].value
|
22
|
+
end
|
23
|
+
|
24
|
+
ret = {}
|
25
|
+
if cfdictionary.value.key?('NS.keys') && cfdictionary.value.key?('NS.objects')
|
26
|
+
keys_and_values = cfdictionary.value['NS.keys'].value.zip(cfdictionary.value['NS.objects'].value)
|
27
|
+
keys_and_values.each do |key, value|
|
28
|
+
ret[self._deserialize_object(key).to_s.to_sym] = self._deserialize_object(value)
|
29
|
+
end
|
30
|
+
elsif cfdictionary.value.key?('NS.objects')
|
31
|
+
return cfdictionary.value['NS.objects'].value.map { |o| self._deserialize_object(o) }
|
32
|
+
else
|
33
|
+
cfdictionary.value.each do |key, value|
|
34
|
+
ret[key.to_sym] = self._deserialize_object(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
|
41
|
+
def _deserialize_object(object)
|
42
|
+
if object.is_a?(CFPropertyList::CFUid)
|
43
|
+
self._deserialize_object(@objects[object.value])
|
44
|
+
elsif object.is_a?(CFPropertyList::CFString) && object.value == '$null'
|
45
|
+
nil
|
46
|
+
elsif object.is_a?(CFPropertyList::CFDictionary)
|
47
|
+
self._deserialize_dictionary(object)
|
48
|
+
elsif object.is_a?(CFPropertyList::CFString) or object.is_a?(CFPropertyList::CFInteger) or object.is_a?(CFPropertyList::CFBoolean)
|
49
|
+
object.value
|
50
|
+
elsif object.is_a?(CFPropertyList::CFArray)
|
51
|
+
object.value.map { |o| self._deserialize_object(o) }
|
52
|
+
else
|
53
|
+
raise "Unsupported object type #{object.class}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def _deserialize_uid(uid)
|
58
|
+
object = @objects[uid.value]
|
59
|
+
|
60
|
+
if object.is_a?(CFPropertyList::CFUid)
|
61
|
+
_deserialize_uid(uid)
|
62
|
+
elsif object.is_a?(CFPropertyList::CFString) && object.value == '$null'
|
63
|
+
nil
|
64
|
+
elsif object.is_a?(CFPropertyList::CFDictionary)
|
65
|
+
hash = {}
|
66
|
+
|
67
|
+
object.value.each do |key, value|
|
68
|
+
if value.is_a?(CFPropertyList::CFUid)
|
69
|
+
hash[key] = self._deserialize_uid(value)
|
70
|
+
else
|
71
|
+
hash[key] = value.value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
hash
|
76
|
+
else
|
77
|
+
object.value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module XcodeResultBundleProcessor
|
2
|
+
class IndentedStringBuffer
|
3
|
+
def initialize(buffer=nil, indent_level=0)
|
4
|
+
@buffer = buffer || ''
|
5
|
+
@indent_spaces = 2
|
6
|
+
@indent_level = indent_level
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(arg)
|
10
|
+
Array(arg).each do |line|
|
11
|
+
@buffer << ' ' * (@indent_spaces * @indent_level) << line
|
12
|
+
@buffer << "\n" unless line.end_with?("\n")
|
13
|
+
@buffer
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_newline
|
19
|
+
@buffer << "\n"
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def indent
|
24
|
+
IndentedStringBuffer.new(@buffer, @indent_level + 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return "\n" if @buffer.empty?
|
29
|
+
@buffer
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|