rspec_junit_formatter 0.6.1 → 0.7.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 892c4451f42f7fdbfe335df86dd6f910d5fc4a54b91614b2da4283490a635646
4
- data.tar.gz: 870ebdab7dd2e600b57493ce26437cee64b73cdc1cd05b48d6f67ad7160e98e8
3
+ metadata.gz: 6a808950ca0ea8273d57ec14b76e0f33326f2cf473708c8809ce7f80d5daef1f
4
+ data.tar.gz: 724eb19bbed6395719c2791f5661a80f5ece271bf85eb44a1d864a883f06edde
5
5
  SHA512:
6
- metadata.gz: 4b55d404b3e056fd85dcef4712c386de35448f442abbc750b8fe9fc5d9953b79bb9552790bff53f88f67a91d7685765d9ddbb9cbefba0317d7cd2c558dabd036
7
- data.tar.gz: aa4f7f1061266efeb3e4da1756d5a47969c8f1964174dbba23054b580a035a356d8b8880f2da47b940f423a5f2fb35371eed52990ab2540f133f49708bbe2bf5
6
+ metadata.gz: 9bf5b02431be19674c48a308132008bdd93d1619a1efadd197009cd67c150984cdefc918f73209535a936f53dd3847b677c9f1c4823a181cc4e18e5f33411c27
7
+ data.tar.gz: db0d242d4eaf205bc38c795f2313a29306d8bdc82218940058929d215a5c7650a22406406bcdd9a585b0793cfa862f62302716f21d498270116625e991b0e871
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Build results](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml?branch=main)
4
4
  [![Gem version](http://img.shields.io/gem/v/rspec_junit_formatter.svg)](https://rubygems.org/gems/rspec_junit_formatter)
5
5
 
6
- [RSpec][rspec] 2 & 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], [Gitlab][gitlab-junit], and probably more, too.
6
+ [RSpec][rspec] 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], [Gitlab][gitlab-junit], and probably more, too.
7
7
 
8
8
  [rspec]: http://rspec.info/
9
9
  [jenkins-junit]: https://jenkins.io/doc/pipeline/steps/junit/
@@ -11,6 +11,22 @@
11
11
  [circleci-junit]: https://circleci.com/docs/2.0/collect-test-data/
12
12
  [gitlab-junit]: https://docs.gitlab.com/ee/ci/unit_test_reports.html#ruby-example
13
13
 
14
+ ## Supported versions and legacy upgrades
15
+
16
+ **0.7.0 requires Ruby >= 3.3 and RSpec >= 3.13, < 4.** We support upstream-maintained stable Ruby branches, including security maintenance, and the maintained stable RSpec minor series. As of October 2026, [Ruby 3.3 is security-maintained and Ruby 3.4/4.0 are normally maintained](https://www.ruby-lang.org/en/downloads/branches/). [RSpec supports only the latest major/minor pair](https://rspec.info/blog/2024/11/introducing-the-monorepo/), currently 3.13; RSpec 4 prereleases are not supported. Future minor releases may raise these minimums when upstream maintenance ends.
17
+
18
+ **[0.6.2](https://rubygems.org/gems/rspec_junit_formatter/versions/0.6.2) is the final legacy-compatible release.** It retains Ruby >= 2.3 and RSpec >= 2, < 4 (except 2.12.0), including the encoding and aggregate failure fixes. See its [compatibility table](https://github.com/sj26/rspec_junit_formatter/blob/v0.6.2/README.md#tested-versus-permitted-versions) for tested combinations and limitations. It does not make end-of-life Ruby or RSpec versions maintained again.
19
+
20
+ If you must keep an older Ruby **or** an older RSpec, use:
21
+
22
+ ```ruby
23
+ gem "rspec_junit_formatter", "0.6.2", require: false
24
+ ```
25
+
26
+ Then run `bundle update rspec_junit_formatter`. Without Bundler, use `gem install rspec_junit_formatter -v 0.6.2`. Upgrading Ruby alone is not enough if your project must keep an older RSpec.
27
+
28
+ To use 0.7.0, upgrade Ruby to a maintained version and RSpec to 3.13, remove the legacy formatter pin, and run `bundle update rspec rspec_junit_formatter`. Both Ruby and `rspec-core` requirements are recorded in the package so dependency resolution can retain 0.6.2 for older environments.
29
+
14
30
  ## Usage
15
31
 
16
32
  Install the gem:
@@ -95,18 +111,113 @@ Note that this example captures all output from every example all the time, pote
95
111
  ## Caveats
96
112
 
97
113
  * XML can only represent a [limited subset of characters][xml-charsets] which excludes null bytes and most control characters. This gem will use character entities where possible and fall back to replacing invalid characters with Ruby-like escape codes otherwise. For example, the null byte becomes `\0`.
114
+ * When converting report text to UTF-8 for XML output, invalid byte sequences and characters with no UTF-8 conversion are replaced with `�` (U+FFFD REPLACEMENT CHARACTER). The formatter uses each string's declared encoding; it does not guess the encoding of binary data.
98
115
 
99
116
  [xml-charsets]: https://www.w3.org/TR/xml/#charsets
100
117
 
101
118
  ## Development
102
119
 
103
- Run the specs with `bundle exec rake`, which uses [Appraisal][appraisal] to run the specs against all supported versions of rspec.
120
+ Use Ruby 3.4 (see [`.ruby-version`](.ruby-version)) for maintenance. This selects the development Ruby; the installed gem's minimum is defined in the [gemspec](rspec_junit_formatter.gemspec).
121
+
122
+ Install the Bundler version recorded in `Gemfile.lock`, then test the locked development bundle:
123
+
124
+ ```sh
125
+ gem install bundler -v 4.0.23
126
+ bundle install
127
+ bundle exec rake spec
128
+ ```
129
+
130
+ The [development workflow](.github/workflows/development.yml) tests this bundle separately from the [compatibility workflow](.github/workflows/ci.yml). Use `bundle update --all` to refresh the development lockfile.
131
+
132
+ The supported RSpec series is also defined by [Appraisal][appraisal], with a separate Gemfile to test dependency resolution without the root lockfile:
133
+
134
+ ```sh
135
+ BUNDLE_GEMFILE=gemfiles/rspec_3_13.gemfile bundle install
136
+ BUNDLE_GEMFILE=gemfiles/rspec_3_13.gemfile bundle exec rake spec
137
+ ```
138
+
139
+ Plain `bundle exec rake` also runs the Appraisals outside CI.
104
140
 
105
141
  [appraisal]: https://github.com/thoughtbot/appraisal
106
142
 
143
+ ### Tested versus permitted versions
144
+
145
+ The [gemspec](rspec_junit_formatter.gemspec) permits Ruby >= 3.3 and `rspec-core` >= 3.13, < 4. These requirements allow installation; future versions are not automatically tested or supported.
146
+
147
+ The CI workflows test these combinations:
148
+
149
+ | Ruby | RSpec | Bundle |
150
+ | --- | --- | --- |
151
+ | 3.3, 3.4 and 4.0 | 3.13, latest patch | Appraisal |
152
+ | 3.3, 3.4 and 4.0 | 3.13, pinned by `Gemfile.lock` | Development |
153
+
154
+ CI selects the latest patch of each listed Ruby branch. The locked bundle currently uses RSpec 3.13.2 and rspec-core 3.13.6. Older Ruby and RSpec combinations remain available in [0.6.2](https://github.com/sj26/rspec_junit_formatter/tree/v0.6.2), not in the current test matrix.
155
+
107
156
  ## Releasing
108
157
 
109
- Bump the gem version in the gemspec, and commit. Then `bundle exec rake build` to build a gem package, `bundle exec rake install` to install and test it locally, then `bundle exec rake release` to tag and push the commits and gem.
158
+ Releases use the [Release workflow](https://github.com/sj26/rspec_junit_formatter/actions/workflows/release.yml)
159
+ and [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/).
160
+ GitHub Actions builds **unsigned gems**: it needs no personal signing key, persistent
161
+ RubyGems API key, or maintainer machine. Local `bundle exec rake build`, `install`,
162
+ and `release` tasks remain available; local releases may retain the maintainer's
163
+ traditional gem signatures. Do not publish the same version through both paths.
164
+
165
+ 1. Bump the gemspec version and update the changelog in a reviewed PR. Merge it
166
+ into `main`. Use a new stable `X.Y.Z` version; never reuse a published version.
167
+ 2. Tag that exact commit as `vX.Y.Z` and push the tag. Record its full commit SHA
168
+ with `git rev-parse 'vX.Y.Z^{commit}'`. Pushing a tag alone does not publish.
169
+ 3. Run the Release workflow **from `main`**, supplying the tag and full SHA. Leave
170
+ `publish` unchecked for validation only. It checks tag/version/commit agreement
171
+ and main ancestry, runs the compatibility and development suites at that SHA,
172
+ builds the gem, rejects embedded signatures, and tests a clean installation
173
+ outside the checkout. PRs also exercise the package build/install check, but
174
+ their artifacts can never be published by this workflow.
175
+ 4. Run again with the same tag and SHA and `publish` checked. After the checks pass,
176
+ review the commit and package SHA-256 in the build log, then approve the
177
+ `release` environment deployment. Sam can approve his own run. The publish job
178
+ downloads that run's exact immutable artifact, uses short-lived OIDC credentials,
179
+ and does not check out or build project code.
180
+ 5. The workflow verifies that RubyGems serves identical bytes before publishing a
181
+ GitHub release with the same gem. Check both the
182
+ [RubyGems version](https://rubygems.org/gems/rspec_junit_formatter/versions) and
183
+ [GitHub release](https://github.com/sj26/rspec_junit_formatter/releases).
184
+
185
+ ### One-time configuration
186
+
187
+ The GitHub `release` environment must require approval by `sj26`, allow
188
+ self-approval, disable admin bypass, and allow deployments only from the `main`
189
+ branch. The **Immutable release tags** repository ruleset blocks updates and
190
+ deletion of `v*` tags, with no bypass actors. Under the existing gem's
191
+ **Trusted publishers** on RubyGems, configure:
192
+
193
+ | Field | Value |
194
+ | --- | --- |
195
+ | Repository owner | `sj26` |
196
+ | Repository name | `rspec_junit_formatter` |
197
+ | Workflow filename | `release.yml` |
198
+ | Environment | `release` |
199
+
200
+ Leave reusable-workflow repository fields blank. This workflow grants
201
+ `id-token: write` only to the environment-protected publish job. Do not add a
202
+ RubyGems API key or personal signing key to repository secrets.
203
+
204
+ ### Recovery
205
+
206
+ Release runs are serialized and never auto-cancel an active publication. If a
207
+ push succeeds but a later step fails, **re-run failed jobs in the original run**
208
+ while its `release-gem` artifact is retained (30 days). This reuses the validated
209
+ bytes and requires environment approval again. An existing RubyGems version is
210
+ accepted only if its SHA-256 matches, and its downloaded bytes must also match.
211
+ An incomplete GitHub draft can resume; an existing asset must match byte-for-byte
212
+ and is never overwritten. A published GitHub release missing its gem fails closed
213
+ and needs manual inspection rather than silently changing a public release.
214
+
215
+ If the tag moved, a digest differs, or the original artifact expired, stop and
216
+ investigate. Do not delete/recreate tags, yank/re-push gems, or overwrite release
217
+ assets to force a retry. A fresh run rebuilds and is safe only if it reproduces
218
+ the same bytes; otherwise recover the original artifact or use a new version
219
+ through the normal review process. Validation-only runs do not test OIDC exchange
220
+ or publication; only a real, explicitly approved new release can prove those paths.
110
221
 
111
222
  ## License
112
223
 
@@ -46,12 +46,7 @@ private
46
46
  end
47
47
 
48
48
  def error_count
49
- # Introduced in rspec 3.6
50
- if @summary_notification.respond_to?(:errors_outside_of_examples_count)
51
- @summary_notification.errors_outside_of_examples_count
52
- else
53
- 0
54
- end
49
+ @summary_notification.errors_outside_of_examples_count
55
50
  end
56
51
 
57
52
  def result_of(notification)
@@ -125,19 +120,8 @@ private
125
120
 
126
121
  # Completely gross hack for absolutely forcing off colorising for the
127
122
  # duration of a block.
128
- if RSpec.configuration.respond_to?(:color_mode=)
129
- def without_color(&block)
130
- swap_rspec_configuration(:color_mode, :off, &block)
131
- end
132
- elsif RSpec.configuration.respond_to?(:color=)
133
- def without_color(&block)
134
- swap_rspec_configuration(:color, false, &block)
135
- end
136
- else
137
- warn 'rspec_junit_formatter cannot prevent colorising due to an unexpected RSpec.configuration format'
138
- def without_color
139
- yield
140
- end
123
+ def without_color(&block)
124
+ swap_rspec_configuration(:color_mode, :off, &block)
141
125
  end
142
126
 
143
127
  def stdout_for(example_notification)
@@ -149,23 +133,9 @@ private
149
133
  end
150
134
 
151
135
  def aggregate_failure?(exception)
152
- # Introduced in rspec 3.3
136
+ # rspec-expectations is optional; the formatter only depends on rspec-core.
153
137
  return false unless defined?(RSpec::Expectations::MultipleExpectationsNotMetError)
154
138
 
155
139
  exception.is_a?(RSpec::Expectations::MultipleExpectationsNotMetError)
156
140
  end
157
141
  end
158
-
159
- # rspec-core 3.0.x forgot to mark this as a module function which causes:
160
- #
161
- # NoMethodError: undefined method `wrap' for RSpec::Core::Notifications::NullColorizer:Class
162
- # .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:229:in `add_shared_group_line'
163
- # .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:157:in `message_lines'
164
- #
165
- if defined?(RSpec::Core::Notifications::NullColorizer) && RSpec::Core::Notifications::NullColorizer.is_a?(Class) && !RSpec::Core::Notifications::NullColorizer.respond_to?(:wrap)
166
- RSpec::Core::Notifications::NullColorizer.class_eval do
167
- def self.wrap(*args)
168
- new.wrap(*args)
169
- end
170
- end
171
- end
@@ -9,8 +9,6 @@ require "rspec/core/formatters/base_formatter"
9
9
  # Dumps rspec results as a JUnit XML file.
10
10
  # Based on XML schema: http://windyroad.org/dl/Open%20Source/JUnit.xsd
11
11
  class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
12
- # rspec 2 and 3 implements are in separate files.
13
-
14
12
  private
15
13
 
16
14
  def xml_dump
@@ -166,7 +164,7 @@ private
166
164
 
167
165
  def escape(text)
168
166
  # Make sure it's utf-8, replace illegal characters with ruby-like escapes, and replace special and discouraged characters with entities
169
- text.to_s.encode(Encoding::UTF_8).gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
167
+ text.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "\uFFFD").gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
170
168
  end
171
169
 
172
170
  STRIP_DIFF_COLORS_BLOCK_REGEXP = /^ ( [ ]* ) Diff: (?: \e\[ 0 m )? (?: \n \1 \e\[ \d+ (?: ; \d+ )* m .* )* /x
@@ -188,8 +186,4 @@ end
188
186
 
189
187
  RspecJunitFormatter = RSpecJUnitFormatter
190
188
 
191
- if Gem::Version.new(RSpec::Core::Version::STRING) >= Gem::Version.new("3")
192
- require "rspec_junit_formatter/rspec3"
193
- else
194
- require "rspec_junit_formatter/rspec2"
195
- end
189
+ require "rspec_junit_formatter/rspec3"
metadata CHANGED
@@ -1,33 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_junit_formatter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Cochran
8
8
  bindir: bin
9
- cert_chain:
10
- - |
11
- -----BEGIN CERTIFICATE-----
12
- MIIDKDCCAhCgAwIBAgIBBTANBgkqhkiG9w0BAQsFADA6MQ0wCwYDVQQDDARzajI2
13
- MRQwEgYKCZImiZPyLGQBGRYEc2oyNjETMBEGCgmSJomT8ixkARkWA2NvbTAeFw0y
14
- NjEwMjAyMzA3MDhaFw0yNzEwMjAyMzA3MDhaMDoxDTALBgNVBAMMBHNqMjYxFDAS
15
- BgoJkiaJk/IsZAEZFgRzajI2MRMwEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkq
16
- hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr60Eo/ttCk8GMTMFiPr3GoYMIMFvLak
17
- xSmTk9YGCB6UiEePB4THSSA5w6IPyeaCF/nWkDp3/BAam0eZMWG1IzYQB23TqIM0
18
- 1xzcNRvFsn0aQoQ00k+sj+G83j3T5OOV5OZIlu8xAChMkQmiPd1NXc6uFv+Iacz7
19
- kj+CMsI9YUFdNoU09QY0b+u+Rb6wDYdpyvN60YC30h0h1MeYbvYZJx/iZK4XY5zu
20
- 4O/FL2ChjL2CPCpLZW55ShYyrzphWJwLOJe+FJ/ZBl6YXwrzQM9HKnt4titSNvyU
21
- KzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/OQQIDAQABozkw
22
- NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU99dfRjEKFyczTeIz
23
- m3ZsDWrNC80wDQYJKoZIhvcNAQELBQADggEBAEHT9KAaQy80m6KKiJiuVVq96HNo
24
- sub7+06IGfsNqxJHJPZoqsdRUkH+WUliVdQ1yAI35QmO2glCJt1UvQU7vkI6CQBB
25
- rjFWNmbQhyK2fjiwuvSI1QmgH/NeT45lzW4TSg04hmIhtX8G+1HeyKck/qDaDyoO
26
- GgETed1QI+mcbxl1+O8w1QSuvClVm7y+Nk5PeOxjPT/2DMuChLyTP3NYnyiHifdS
27
- OKO8Px6knr64z1LWwB+m04k+z+D1oD6757fkD96O1C1AtMtNoQHwfNX7JSPhQSQM
28
- z4nsMtYoPwFoGnDqvraRmVqMLL3asgAh8SYE6c4eoaX1b0r83v6aLnIh3ew=
29
- -----END CERTIFICATE-----
30
- date: 1980-01-02 00:00:00.000000000 Z
9
+ cert_chain: []
10
+ date: 2026-10-21 00:00:00.000000000 Z
31
11
  dependencies:
32
12
  - !ruby/object:Gem::Dependency
33
13
  name: rspec-core
@@ -35,26 +15,20 @@ dependencies:
35
15
  requirements:
36
16
  - - ">="
37
17
  - !ruby/object:Gem::Version
38
- version: '2'
18
+ version: '3.13'
39
19
  - - "<"
40
20
  - !ruby/object:Gem::Version
41
21
  version: '4'
42
- - - "!="
43
- - !ruby/object:Gem::Version
44
- version: 2.12.0
45
22
  type: :runtime
46
23
  prerelease: false
47
24
  version_requirements: !ruby/object:Gem::Requirement
48
25
  requirements:
49
26
  - - ">="
50
27
  - !ruby/object:Gem::Version
51
- version: '2'
28
+ version: '3.13'
52
29
  - - "<"
53
30
  - !ruby/object:Gem::Version
54
31
  version: '4'
55
- - - "!="
56
- - !ruby/object:Gem::Version
57
- version: 2.12.0
58
32
  - !ruby/object:Gem::Dependency
59
33
  name: bundler
60
34
  requirement: !ruby/object:Gem::Requirement
@@ -140,7 +114,6 @@ files:
140
114
  - LICENSE
141
115
  - README.md
142
116
  - lib/rspec_junit_formatter.rb
143
- - lib/rspec_junit_formatter/rspec2.rb
144
117
  - lib/rspec_junit_formatter/rspec3.rb
145
118
  homepage: https://github.com/sj26/rspec_junit_formatter
146
119
  licenses:
@@ -154,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
154
127
  requirements:
155
128
  - - ">="
156
129
  - !ruby/object:Gem::Version
157
- version: 2.3.0
130
+ version: 3.3.0
158
131
  required_rubygems_version: !ruby/object:Gem::Requirement
159
132
  requirements:
160
133
  - - ">="
checksums.yaml.gz.sig DELETED
@@ -1,2 +0,0 @@
1
- ;�c��M���L���i�`-%uE���^o�_��+�=^� ���(L�֏!m �?�h+��W<c
2
- )���@,��v�R��x䆎���Uc
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
4
- attr_reader :started
5
-
6
- def start(example_count)
7
- @started = Time.now
8
- super
9
- end
10
-
11
- def dump_summary(duration, example_count, failure_count, pending_count)
12
- super
13
- xml_dump
14
- end
15
-
16
- private
17
-
18
- def result_of(example)
19
- example.execution_result[:status].to_sym
20
- end
21
-
22
- def example_group_file_path_for(example)
23
- meta = example.metadata
24
- while meta[:example_group]
25
- meta = meta[:example_group]
26
- end
27
- meta[:file_path]
28
- end
29
-
30
- def classname_for(example)
31
- fp = example_group_file_path_for(example)
32
- fp.sub(%r{\.[^/.]+\Z}, "").gsub("/", ".").gsub(/\A\.+|\.+\Z/, "")
33
- end
34
-
35
- def duration_for(example)
36
- example.execution_result[:run_time]
37
- end
38
-
39
- def description_for(example)
40
- example.full_description
41
- end
42
-
43
- def exception_for(example)
44
- example.execution_result[:exception]
45
- end
46
-
47
- def failure_type_for(example)
48
- exception_for(example).class.name
49
- end
50
-
51
- def failure_message_for(example)
52
- strip_diff_colors(exception_for(example).to_s)
53
- end
54
-
55
- def failure_for(example)
56
- exception = exception_for(example)
57
- message = strip_diff_colors(exception.message)
58
- backtrace = format_backtrace(exception.backtrace, example)
59
-
60
- if shared_group = find_shared_group(example)
61
- backtrace << "Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\" called from #{shared_group.metadata[:example_group][:location]}"
62
- end
63
-
64
- "#{message}\n#{backtrace.join("\n")}"
65
- end
66
-
67
- def error_count
68
- 0
69
- end
70
-
71
- def find_shared_group(example)
72
- group_and_parent_groups(example).find { |group| group.metadata[:shared_group_name] }
73
- end
74
-
75
- def group_and_parent_groups(example)
76
- example.example_group.parent_groups + [example.example_group]
77
- end
78
-
79
- def stdout_for(example)
80
- example.metadata[:stdout]
81
- end
82
-
83
- def stderr_for(example)
84
- example.metadata[:stderr]
85
- end
86
- end
data.tar.gz.sig DELETED
Binary file
metadata.gz.sig DELETED
Binary file