From 962a7d1fe0a43b5fb1f3378e626731e1625667e2 Mon Sep 17 00:00:00 2001 From: Alex Gittemeier Date: Tue, 5 Jul 2022 23:54:08 -0500 Subject: [PATCH 1/5] Migrate unit tests to rspec Also in this commit I added a rubocop config since I am now making actually significant code changes. To that end, I've updated copyright headers for files I've added/touched, as well as run `rubocop -A` on them. This trend will continue as more files come into the fold. --- .gitignore | 11 +- .rspec | 2 + .rubocop.yml | 67 +++++ Gemfile.lock | 54 +++- LICENSE | 2 +- Rakefile | 71 +++-- ruby-xz.gemspec | 15 +- .../minitar/integration_spec.rb | 57 ++-- spec/spec_helper.rb | 76 +++++ {test => spec}/test-data/iso88591.txt.xz | Bin {test => spec}/test-data/lorem_ipsum.txt | 0 {test => spec}/test-data/lorem_ipsum.txt.xz | Bin spec/xz/stream_reader_spec.rb | 280 ++++++++++++++++++ spec/xz/stream_writer_spec.rb | 143 +++++++++ spec/xz_spec.rb | 141 +++++++++ test/common.rb | 35 --- test/test_stream_reader.rb | 249 ---------------- test/test_stream_writer.rb | 130 -------- test/test_xz.rb | 122 -------- 19 files changed, 840 insertions(+), 615 deletions(-) create mode 100644 .rspec create mode 100644 .rubocop.yml rename test/test_tarball.rb => spec/minitar/integration_spec.rb (55%) create mode 100644 spec/spec_helper.rb rename {test => spec}/test-data/iso88591.txt.xz (100%) rename {test => spec}/test-data/lorem_ipsum.txt (100%) rename {test => spec}/test-data/lorem_ipsum.txt.xz (100%) create mode 100644 spec/xz/stream_reader_spec.rb create mode 100644 spec/xz/stream_writer_spec.rb create mode 100755 spec/xz_spec.rb delete mode 100644 test/common.rb delete mode 100644 test/test_stream_reader.rb delete mode 100644 test/test_stream_writer.rb delete mode 100755 test/test_xz.rb diff --git a/.gitignore b/.gitignore index 10b0482..39cc449 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ /*.gem -doc -test/test-data/lorem2.txt.xz -pkg/ +/.bundle +/.yardoc +/doc +/pkg +/spec/.status +/spec/coverage +/spec/test-data/lorem2.txt.xz +/spec/test-data/testtarball.tar.xz diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..3ac6b84 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,67 @@ +require: rubocop-rspec + +AllCops: + TargetRubyVersion: 2.3.0 + NewCops: enable + Exclude: + - 'node_modules/**/*' + - 'tmp/**/*' + - 'vendor/**/*' + - '.git/**/*' + +Layout/ArgumentAlignment: + EnforcedStyle: with_fixed_indentation +Layout/EmptyLineBetweenDefs: + AllowAdjacentOneLineDefs: true +Layout/EmptyLines: # Directly conflicts with YARDs only way to suppress unwanted + Enabled: false # comments, such as copyright headers +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent +Layout/FirstHashElementIndentation: + EnforcedStyle: consistent +Layout/LineEndStringConcatenationIndentation: + EnforcedStyle: indented +Layout/SpaceInsideBlockBraces: + SpaceBeforeBlockParameters: false +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: no_space + +Lint/NumberConversion: + Enabled: true +Lint/Void: + CheckForMethodsWithNoSideEffects: true +Lint/AmbiguousOperatorPrecedence: + Enabled: false + +Metrics/AbcSize: + CountRepeatedAttributes: false +Metrics/BlockLength: + IgnoredMethods: + - describe + +Naming/MethodParameterName: + AllowedNames: [x, y, at, by, id, in, of, on, to] + +RSpec/MultipleExpectations: + Enabled: false +RSpec/ExampleLength: + Max: 10 +RSpec/DescribedClass: + EnforcedStyle: explicit + +Style/AsciiComments: + Enabled: false +Style/FormatStringToken: + Enabled: false +Style/NegatedIf: + Enabled: false +Style/NumericLiterals: + MinDigits: 7 +Style/NumericPredicate: + EnforcedStyle: comparison +Style/SymbolArray: + EnforcedStyle: brackets +Style/TrailingCommaInArrayLiteral: + Enabled: false +Style/WordArray: + MinSize: 4 diff --git a/Gemfile.lock b/Gemfile.lock index c8bab77..01a83ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,9 +6,55 @@ PATH GEM remote: https://rubygems.org/ specs: + ast (2.4.2) + diff-lcs (1.5.0) + docile (1.4.0) + json (2.6.2) minitar (0.9) - minitest (5.15.0) + parallel (1.22.1) + parser (3.1.2.0) + ast (~> 2.4.1) + rainbow (3.1.1) rake (13.0.6) + regexp_parser (2.5.0) + rexml (3.2.5) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) + rubocop (1.31.1) + json (~> 2.3) + parallel (~> 1.10) + parser (>= 3.1.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.18.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.18.0) + parser (>= 3.1.1.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (2.12.1) + rubocop (~> 1.31) + ruby-progressbar (1.11.0) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + unicode-display_width (2.2.0) PLATFORMS x86_64-darwin-21 @@ -16,9 +62,13 @@ PLATFORMS DEPENDENCIES minitar (~> 0.6) - minitest (~> 5.14) rake (~> 13.0) + rspec (~> 3.11) + rubocop (~> 1.31) + rubocop-rake (~> 0.6.0) + rubocop-rspec (~> 2.12) ruby-xz! + simplecov (~> 0.21.2) BUNDLED WITH 2.3.7 diff --git a/LICENSE b/LICENSE index 69812d9..d7ed50e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2011-2018 Marvin Gülker et al. +Copyright © 2011-2022 Marvin Gülker et al. See AUTHORS for the full list of contributors. diff --git a/Rakefile b/Rakefile index b1ab4bb..7670f26 100644 --- a/Rakefile +++ b/Rakefile @@ -1,48 +1,47 @@ -# -*- mode: ruby; coding: utf-8 -*- -=begin -Basic liblzma-bindings for Ruby. +# -*- mode: ruby -*- +# frozen_string_literal: true + +# Basic liblzma-bindings for Ruby. +# +# Copyright © 2011-2022 Alex Gittemeier et al. +# +# See AUTHORS for the full list of contributors. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the ‘Software’), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. -Copyright © 2011-2018 Marvin Gülker et al. - -See AUTHORS for the full list of contributors. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the ‘Software’), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -=end require 'bundler/setup' -require "rake/testtask" -require "rdoc/task" +require 'rdoc/task' require 'bundler/gem_tasks' +require 'rspec/core/rake_task' # Documentation Rake::RDocTask.new do |rd| - rd.rdoc_files.include("lib/**/*.rb", "*.md", "**/*.rdoc", "LICENSE", "AUTHORS") - rd.title = "ruby-xz RDocs" - rd.main = "README.md" - rd.generator = "hanna" - rd.rdoc_dir = "doc" + rd.rdoc_files.include('lib/**/*.rb', '*.md', '**/*.rdoc', 'LICENSE', 'AUTHORS') + rd.title = 'ruby-xz RDocs' + rd.main = 'README.md' + rd.rdoc_dir = 'doc' end # Testing -Rake::TestTask.new do |t| - t.test_files = FileList["test/test_*.rb"] - t.warning = true +RSpec::Core::RakeTask.new(:spec) do |task| + task.verbose = false end -task default: [:test] +task default: [:spec] diff --git a/ruby-xz.gemspec b/ruby-xz.gemspec index 48991f4..4c0b8ab 100644 --- a/ruby-xz.gemspec +++ b/ruby-xz.gemspec @@ -1,9 +1,9 @@ +# -*- mode: ruby -*- # frozen_string_literal: true -# -*- mode: ruby; coding: utf-8 -*- -#-- + # Basic liblzma-bindings for Ruby. # -# Copyright © 2011-2018 Marvin Gülker et al. +# Copyright © 2011-2022 Alex Gittemeier et al. # # See AUTHORS for the full list of contributors. # @@ -24,7 +24,6 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -#++ require_relative 'lib/xz/version' @@ -39,7 +38,7 @@ GEMSPEC = Gem::Specification.new do |spec| Since fiddle is used to implement the bindings, no compilation is needed. DESCRIPTION - spec.authors = ['Marvin Gülker', 'Alex Gittemeier'] + spec.authors = ['Alex Gittemeier', 'Marvin Gülker'] spec.email = 'me@a.lexg.dev' spec.license = 'MIT' @@ -61,6 +60,10 @@ GEMSPEC = Gem::Specification.new do |spec| spec.post_install_message = 'Version 1.0.0 of ruby-xz breaks the API. Read HISTORY.rdoc and adapt your code to the new API.' spec.add_development_dependency 'minitar', '~> 0.6' - spec.add_development_dependency 'minitest', '~> 5.14' spec.add_development_dependency 'rake', '~> 13.0' + spec.add_development_dependency 'rspec', '~> 3.11' + spec.add_development_dependency 'rubocop', '~> 1.31' + spec.add_development_dependency 'rubocop-rake', '~> 0.6.0' + spec.add_development_dependency 'rubocop-rspec', '~> 2.12' + spec.add_development_dependency 'simplecov', '~> 0.21.2' end diff --git a/test/test_tarball.rb b/spec/minitar/integration_spec.rb similarity index 55% rename from test/test_tarball.rb rename to spec/minitar/integration_spec.rb index d0b386a..c419ad6 100644 --- a/test/test_tarball.rb +++ b/spec/minitar/integration_spec.rb @@ -1,8 +1,8 @@ -# -*- coding: utf-8 -*- -#-- +# frozen_string_literal: true + # Basic liblzma-bindings for Ruby. # -# Copyright © 2011-2018 Marvin Gülker et al. +# Copyright © 2011-2022 Alex Gittemeier et al. # # See AUTHORS for the full list of contributors. # @@ -23,57 +23,52 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -#++ -require "minitar" -require_relative "common" + +require 'xz' +require 'minitar' +require 'pathname' # Create XZ-compressed tarballs and unpack them with the system's # tar(1) utility, and vice-versa. This ensures our library interacts # with the environment as one expects it to. -class TarballTest < Minitest::Test - - def test_pack_tarball - filename = TEST_DIR + "testtarball.tar.xz" - content = File.read(TEST_DIR + "test-data/lorem_ipsum.txt") +RSpec.describe 'this gem' do + it 'works with Minitar.pack' do + filename = TestData::TEST_TARBALL_FILENAME + content = File.read(TestData::PLAINTEXT_FILENAME) XZ::StreamWriter.open(filename) do |txz| - Dir.chdir(TEST_DIR) do # proper file path parts - Minitar.pack("test-data/lorem_ipsum.txt", txz) + Dir.chdir(TestData::PARENT_DIRNAME) do # proper file path parts + Minitar.pack('test-data/lorem_ipsum.txt', txz) end end - Dir.mktmpdir("testtarball") do |dir| + Dir.mktmpdir('testtarball') do |dir| Dir.chdir(dir) do - system("tar -xJf '#{filename}'") - assert File.exist?("test-data/lorem_ipsum.txt"), "compressed file missing!" - assert_equal File.read("test-data/lorem_ipsum.txt"), content + system("tar -xJf '#{filename}'", exception: true) + expect(Pathname('test-data/lorem_ipsum.txt')).to exist + expect(File.read('test-data/lorem_ipsum.txt')).to eq(content) end end - ensure - File.unlink(filename) if File.exist?(filename) end - def test_unpack_tarball - filename = TEST_DIR + "testtarball.tar.xz" - content = File.read(TEST_DIR + "test-data/lorem_ipsum.txt") + it 'works with Minitar.unpack' do + filename = TestData::TEST_TARBALL_FILENAME + content = File.read(TestData::PLAINTEXT_FILENAME) - Dir.chdir(TEST_DIR) do # proper file path parts - system("tar -cJf '#{filename}' test-data/lorem_ipsum.txt") + Dir.chdir(TestData::PARENT_DIRNAME) do # proper file path parts + system("tar -cJf '#{filename}' test-data/lorem_ipsum.txt", exception: true) end - Dir.mktmpdir("testtarball") do |dir| + Dir.mktmpdir('testtarball') do |dir| Dir.chdir(dir) do XZ::StreamReader.open(filename) do |txz| - Minitar.unpack(txz, ".") + Minitar.unpack(txz, '.') end - assert File.exist?("test-data/lorem_ipsum.txt"), "compresed file missing!" - assert_equal File.read("test-data/lorem_ipsum.txt"), content + expect(Pathname('test-data/lorem_ipsum.txt')).to exist + expect(File.read('test-data/lorem_ipsum.txt')).to eq(content) end end - ensure - File.unlink(filename) if File.exist?(filename) end - end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ad6e653 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# Basic liblzma-bindings for Ruby. +# +# Copyright © 2011-2022 Alex Gittemeier et al. +# +# See AUTHORS for the full list of contributors. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the ‘Software’), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +require 'bundler/setup' +require 'simplecov' + +SimpleCov.start do + enable_coverage :branch + coverage_dir File.join(__dir__, 'coverage') +end + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true # default in RSpec 4 + expectations.syntax = :expect + end + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true # default in RSpec 4 + end + config.shared_context_metadata_behavior = :apply_to_host_groups # default in Rspec 4 + + # enable fit, fdescribe, fcontext + config.filter_run_when_matching :focus + + # Enable flags like --only-failures and --next-failure + # Should be enabled only if the entire suite takes too long + # config.example_status_persistence_file_path = 'spec/.status' + + config.disable_monkey_patching! + config.warnings = true + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + # config.profile_examples = 3 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + Kernel.srand config.seed +end + +module TestData + PARENT_DIRNAME = File.expand_path('test-data/..', __dir__) + PLAINTEXT_FILENAME = File.expand_path('test-data/lorem_ipsum.txt', __dir__) + PLAINTEXT_XZ_FILENAME = File.expand_path('test-data/lorem_ipsum.txt.xz', __dir__) + ISO88591_XZ_FILENAME = File.expand_path('test-data/iso88591.txt.xz', __dir__) + LIVE_TEST_FILENAME = File.expand_path('test-data/lorem2.txt.xz', __dir__) + TEST_TARBALL_FILENAME = File.expand_path('test-data/testtarball.tar.xz', __dir__) +end diff --git a/test/test-data/iso88591.txt.xz b/spec/test-data/iso88591.txt.xz similarity index 100% rename from test/test-data/iso88591.txt.xz rename to spec/test-data/iso88591.txt.xz diff --git a/test/test-data/lorem_ipsum.txt b/spec/test-data/lorem_ipsum.txt similarity index 100% rename from test/test-data/lorem_ipsum.txt rename to spec/test-data/lorem_ipsum.txt diff --git a/test/test-data/lorem_ipsum.txt.xz b/spec/test-data/lorem_ipsum.txt.xz similarity index 100% rename from test/test-data/lorem_ipsum.txt.xz rename to spec/test-data/lorem_ipsum.txt.xz diff --git a/spec/xz/stream_reader_spec.rb b/spec/xz/stream_reader_spec.rb new file mode 100644 index 0000000..985a52d --- /dev/null +++ b/spec/xz/stream_reader_spec.rb @@ -0,0 +1,280 @@ +# frozen_string_literal: true + +# Basic liblzma-bindings for Ruby. +# +# Copyright © 2011-2022 Alex Gittemeier et al. +# +# See AUTHORS for the full list of contributors. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the ‘Software’), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +require 'xz' + +RSpec.describe XZ::StreamReader do + it 'allows client code to read to a compressed stream originating from a file' do + File.open(TestData::PLAINTEXT_XZ_FILENAME) do |file| + reader = XZ::StreamReader.new(file) + + expect(reader.read(11)).to eq('Lorem ipsum') + expect(reader.read(15)).to eq(' dolor sit amet') + + rest = reader.read + expect(rest[-28..-1]).to eq("Lorem ipsum dolor sit amet.\n") + expect(reader.read).to eq('') # We’re at EOF + expect(reader).to be_eof + + reader.close + end + end + + describe '.close' do + it 'closes the underlying file' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + reader = XZ::StreamReader.new(file) + reader.read + reader.close + expect(file).to be_closed + end + end + + # TODO: should move to description of .finish + it 'does not close the underlying file when .finish is called' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + reader = XZ::StreamReader.new(file) + reader.read + reader.finish + expect(file).not_to be_closed + end + end + + it 'finishes the stream and closes the underlying file at the end of the block (in block form)' do + #XXX: surprising API: I would have expected the method to passthru the block value + reader = XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME, &:read) + expect(reader).to be_finished + expect(reader.instance_variable_get(:@delegate_io)).to be_closed + end + + + it 'closes the underlying file' do + reader = XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) + reader.read + reader.close + expect(reader.instance_variable_get(:@delegate_io)).to be_closed + end + + + # TODO: should move to description of .finish + it 'does not close the underlying file when .finish is called (when using open)' do + reader = XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) + reader.read + reader.finish + expect(reader.instance_variable_get(:@delegate_io)).not_to be_closed + end + + it 'does not close the underlying file when .rewind is called' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + r = XZ::StreamReader.new(file) + r.read(10) + r.rewind + expect(file).not_to be_closed + end + end + + it 'does not close the underlying file when .rewind is called (when using open)' do + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) do |r| + r.read(10) + r.rewind + expect(r.instance_variable_get(:@delegate_io)).not_to be_closed + end + end + + it 'is okay to close more than once (open in block form)' do + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME, &:close) + end + end + + describe '.finish' do + it 'finishes the stream but does not close the underlying file' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + r = XZ::StreamReader.new(file) + r.read + expect(r.finish).to eq(file) + + expect(r).to be_finished + expect(file).not_to be_closed + end + end + + it 'returns the underlying opened file (with block)' do + file = nil + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) do |r| + r.read + file = r.finish + end + expect(file).to be_kind_of(File) # Return value of #finish + expect(file).not_to be_closed # XXX: surprising API + file.close # cleanup + end + + it 'returns the underlying opened file' do + reader = XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) + reader.read + file = reader.finish + expect(file).to be_kind_of(File) + expect(file).not_to be_closed + file.close # cleanup + end + end + + describe '.open' do + it 'opens a filename and yields a stream containing the uncompressed contents' do + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) do |reader| + expect(reader.read).to eq(File.read(TestData::PLAINTEXT_FILENAME)) + end + end + + # TODO: this should be moved to description of .new + it 'creates a stream containing the uncompressed contents that reads the given file object' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + reader = XZ::StreamReader.new(file) + expect(reader.read).to eq(File.read(TestData::PLAINTEXT_FILENAME)) + reader.close + end + end + end + + describe '.pos' do + it 'returns the offset within the uncompressed data (not the compressed data)' do + text = File.read(TestData::PLAINTEXT_FILENAME) + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) do |reader| + reader.read + expect(reader.pos).to eq(text.bytes.count) + end + end + end + + describe '.rewind' do + it 'rewinds to the beginning of the stream' do + File.open(TestData::PLAINTEXT_XZ_FILENAME, 'rb') do |file| + reader = XZ::StreamReader.new(file) + text = reader.read(10) + reader.rewind + expect(reader.read(10)).to eq(text) + end + end + + it 'rewinds to the beginning of the stream (when opening with block)' do + XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME) do |reader| + text = reader.read(10) + reader.rewind + expect(reader.read(10)).to eq(text) + end + end + end + + it 'handles default encodings correctly' do + enc1 = Encoding.default_external + enc2 = Encoding.default_internal + verb = $VERBOSE + $VERBOSE = nil # Disable warnings, because setting + # Encoding.default_{internal,external} generates a + # warning. However, setting these is required to test + # if they're properly honoured by ruby-xz. + begin + Encoding.default_external = Encoding::ISO_8859_1 + + # Forced binary read must always yield BINARY + XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME) do |reader| + str = reader.read(255) + expect(str.encoding).to eq(Encoding::BINARY) + end + + # Now the external encoding needs to be detected + XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME) do |reader| + str = reader.read + expect(str.encoding).to eq(Encoding::ISO_8859_1) + expect(str).to be_valid_encoding + end + + # Request transcode + XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME, external_encoding: 'ISO-8859-1', + internal_encoding: 'UTF-8') do |reader| + str = reader.read + expect(str.encoding).to eq(Encoding::UTF_8) + expect(str).to be_valid_encoding + end + + # Request transcode via default internal encoding + Encoding.default_internal = Encoding::UTF_8 + XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME) do |reader| + str = reader.read + expect(str.encoding).to eq(Encoding::UTF_8) + expect(str).to be_valid_encoding + end + + # Ensure getc does what it should when asked for multibyte chars + XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME) do |reader| + expect(reader.getc).to eq('B') + expect(reader.getc).to eq('ä') + expect(reader.getc).to eq('r') + end + ensure + # Reset to normal for further tests + Encoding.default_external = enc1 + Encoding.default_internal = enc2 + $VERBOSE = verb + end + end + + describe '.set_encoding' do + specify do + reader = XZ::StreamReader.open(TestData::ISO88591_XZ_FILENAME) + + reader.set_encoding('UTF-8') + expect(reader.external_encoding).to eq(Encoding::UTF_8) + expect(reader.internal_encoding).to be_nil + + reader.set_encoding('ISO-8859-1:UTF-8') + expect(reader.external_encoding).to eq(Encoding::ISO_8859_1) + expect(reader.internal_encoding).to eq(Encoding::UTF_8) + + reader.set_encoding(Encoding::UTF_8) + expect(reader.external_encoding).to eq(Encoding::UTF_8) + expect(reader.internal_encoding).to be_nil + + reader.set_encoding(Encoding::UTF_8, Encoding::ISO_8859_1) + expect(reader.external_encoding).to eq(Encoding::UTF_8) + expect(reader.internal_encoding).to eq(Encoding::ISO_8859_1) + + # TODO: Consider specifying kwargs for this method (and writing tests to prove compatibility) + reader.set_encoding('ISO-8859-1', invalid: :replace, replace: '?') + expect(reader.external_encoding).to eq(Encoding::ISO_8859_1) + expect(reader.internal_encoding).to be_nil + + reader.set_encoding('ISO-8859-1', 'UTF-8', invalid: :replace, replace: '?') + expect(reader.external_encoding).to eq(Encoding::ISO_8859_1) + expect(reader.internal_encoding).to eq(Encoding::UTF_8) + + reader.set_encoding('ISO-8859-1:UTF-8', invalid: :replace, replace: '?') + expect(reader.external_encoding).to eq(Encoding::ISO_8859_1) + expect(reader.internal_encoding).to eq(Encoding::UTF_8) + end + end +end diff --git a/spec/xz/stream_writer_spec.rb b/spec/xz/stream_writer_spec.rb new file mode 100644 index 0000000..b7e2599 --- /dev/null +++ b/spec/xz/stream_writer_spec.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +# Basic liblzma-bindings for Ruby. +# +# Copyright © 2011-2022 Alex Gittemeier et al. +# +# See AUTHORS for the full list of contributors. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the ‘Software’), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +require 'xz' + +# For this test-case, please note that we do not check whether the compressed +# string is bit-perfect equal to a fixed string, because different compression +# options and different versions of liblzma can influence the result. Hence, we +# only test whether the decompressing again with XZ.decompress produces the +# original string. +RSpec.describe XZ::StreamWriter do + it 'allows client code to write to a compressed stream that ultimately writes to a file' do + text = File.read(TestData::PLAINTEXT_FILENAME) + text1 = text[0...10] + text2 = text[10..-1] + + File.open(TestData::LIVE_TEST_FILENAME, 'wb') do |file| + writer = XZ::StreamWriter.new(file) + + expect(writer.write(text1)).to eq(text1.bytes.count) + expect(writer.write(text2)).to eq(text2.bytes.count) + writer.close + + expect(File.stat(TestData::LIVE_TEST_FILENAME).size).to be < text.bytesize + expect(writer).to be_finished + expect(writer).to be_closed + expect { writer.write('foo') }.to raise_error(IOError) + end + + expect(XZ.decompress(File.binread(TestData::LIVE_TEST_FILENAME))).to eq(text) + end + + describe '.close' do + # TODO: should move to description of .finish + it 'does not close the underlying file when .finish is called' do + File.open(TestData::LIVE_TEST_FILENAME, 'wb') do |file| + w = XZ::StreamWriter.new(file) + w.write('Foo') + w.finish + expect(file).not_to be_closed + end + end + + it 'finishes the stream and closes the underlying file when .close is called' do + File.open(TestData::LIVE_TEST_FILENAME, 'wb') do |file| + w = XZ::StreamWriter.new(file) + w.write('Foo') + w.close + expect(w).to be_finished + expect(file).to be_closed + end + end + + it 'finishes the stream and closes the underlying file at the end of the block (in block form)' do + #XXX: surprising API: I would have expected the method to passthru the block value + writer = XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) {|w| w.write('Foo') } + expect(writer).to be_finished + expect(writer.instance_variable_get(:@delegate_io)).to be_closed + end + + it 'finishes the streamand closes the underlying file' do + writer = XZ::StreamWriter.new(File.open(TestData::LIVE_TEST_FILENAME, 'wb')) + writer.write('Foo') + writer.close + expect(writer).to be_finished + expect(writer.instance_variable_get(:@delegate_io)).to be_closed + end + + it 'is okay to close more than once (open in block form)' do + # Test double closing (this should not raise) + XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) do |w| + w.write('Foo') + w.close + end + end + end + + describe '.finish' do + it 'returns the underlying file' do + File.open(TestData::LIVE_TEST_FILENAME, 'wb') do |file| + w = XZ::StreamWriter.new(file) + w.write('Foo') + + expect(w.finish).to eq(file) + expect(file).not_to be_closed # TODO: (remove) this is already asserted elsewhere + end + end + + it 'returns the underlying opened file (with block)' do + file = nil + XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) do |w| + w.write('Foo') + file = w.finish + end + expect(file).to be_kind_of(File) # Return value of #finish + expect(file).not_to be_closed # XXX: surprising API + file.close # cleanup + end + + it 'returns the underlying opened file' do + writer = XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) + writer.write('Foo') + file = writer.finish + expect(file).to be_kind_of(File) + expect(file).not_to be_closed + file.close # cleanup + end + end + + it 'produces a .xz file that round-trips with XZ.decompress' do + text = File.read(TestData::PLAINTEXT_FILENAME) + + XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) do |file| + file.write(text) + end + + expect(XZ.decompress(File.binread(TestData::LIVE_TEST_FILENAME))).to eq(text) + end +end diff --git a/spec/xz_spec.rb b/spec/xz_spec.rb new file mode 100755 index 0000000..3e69c30 --- /dev/null +++ b/spec/xz_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +# Basic liblzma-bindings for Ruby. +# +# Copyright © 2011-2022 Alex Gittemeier et al. +# +# See AUTHORS for the full list of contributors. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the ‘Software’), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software +# is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +require 'xz' +require 'tempfile' + +RSpec.describe XZ do + let(:test_xz) do + "\xFD7zXZ\x00\x00\x04\xE6\xD6\xB4F\x02\x00!\x01" \ + "\x16\x00\x00\x00t/\xE5\xA3\xE0\x00\x13\x00\x10]\x00\x18" \ + "\fB\x92jg\xBC\x0E\xD132a\xD6|\x00\x00\x00" \ + "\x0F:\xFE\xFB\"1\xB8\xB6\x00\x01,\x14\xF8\nm\x03" \ + "\x1F\xB6\xF3}\x01\x00\x00\x00\x00\x04YZ" + end + + describe 'self.decompress' do + it 'decompresses an XZ archive' do + expect(XZ.decompress(test_xz)).to eq('01234567890123456789') + end + + it 'decompresses to Encoding.default_external by default' do + expect(XZ.decompress(test_xz).encoding).to eq(Encoding.default_external) + end + + it 'encodes the data with ISO-8859-1 when specified via `external_encoding:`' do + str = XZ.decompress(test_xz, external_encoding: 'ISO-8859-1') + expect(str.encoding).to eq(Encoding::ISO_8859_1) + end + + it 'transcodes the data when internal and external encodings are both specified' do + str = XZ.decompress(test_xz, external_encoding: 'ISO-8859-1', internal_encoding: 'UTF-16LE') + expect(str.encoding).to eq(Encoding::UTF_16LE) + end + + it 'requires an external encoding if transcoding is requested (i.e. by specifying `internal_encoding:`)' do + expect { XZ.decompress(test_xz, internal_encoding: 'UTF-8') }.to raise_error(ArgumentError) + end + + it 'raises XZ::LZMAError when provided invalid input' do + corrupt_xz = test_xz.dup + corrupt_xz[20] = "\x13" + expect { XZ.decompress(corrupt_xz) }.to raise_error(XZ::LZMAError) + end + end + + + describe 'self.compress' do + it 'compresses a string into an XZ archive that can be read by the system `xzcat`' do + str = '01234567890123456789' + tmp = XZ.compress(str) + expect(tmp[0, 5].bytes.to_a).to eq("\xFD7zXZ".bytes.to_a) + + IO.popen('xzcat', 'w+') do |io| + io.write(tmp) + io.close_write + expect(io.read).to eq(str) + end + end + + it 'accepts compression levels 0 upto 9, with and without extreme option' do + str = 'Once upon a time, there was...' + + 0.upto(9) do |i| + [true, false].each do |extreme| + compressed = XZ.compress(str, level: i, extreme: extreme) + expect(XZ.decompress(compressed)).to eq(str) + end + end + end + + it 'refuses compression levels larger than 9' do + expect { XZ.compress('foo', level: 10) }.to raise_error(ArgumentError) + expect { XZ.compress('foo', level: 15) }.to raise_error(ArgumentError) + end + + it 'produces output that round-trips via self.decompress' do + str = 'Once upon a time, there was...' + expect(XZ.decompress(XZ.compress(str))).to eq(str) + end + end + + describe 'self.compress_file' do + it 'saves a compressed version of one file to a different file' do + Tempfile.open('in') do |in_file| + in_file.write('01234567890123456789') + in_file.close + + Tempfile.open('out') do |out_file| + out_file.close + + XZ.compress_file(in_file.path, out_file.path) + + out_file.open + expect(out_file.read[0, 5].bytes.to_a).to eq("\xFD7zXZ".bytes.to_a) + end + end + end + end + + describe 'self.decompress_file' do + it 'saves a decompressed version of an .xz file to a different file' do + Tempfile.open('in') do |in_file| + in_file.write(test_xz) + in_file.close + + Tempfile.open('out') do |out_file| + out_file.close + + XZ.decompress_file(in_file.path, out_file.path) + + out_file.open + expect(out_file.read).to eq('01234567890123456789') + end + end + end + end +end diff --git a/test/common.rb b/test/common.rb deleted file mode 100644 index 511282e..0000000 --- a/test/common.rb +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -#-- -# Basic liblzma-bindings for Ruby. -# -# Copyright © 2011-2018 Marvin Gülker et al. -# -# See AUTHORS for the full list of contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the ‘Software’), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the Software -# is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -#++ - -require_relative "../lib/xz" - -require "pathname" -require "tempfile" -require "minitest/autorun" - -# This is the absolute path to the test/ directory. -TEST_DIR = Pathname.new(__FILE__).dirname diff --git a/test/test_stream_reader.rb b/test/test_stream_reader.rb deleted file mode 100644 index 884048b..0000000 --- a/test/test_stream_reader.rb +++ /dev/null @@ -1,249 +0,0 @@ -# -*- coding: utf-8 -*- -#-- -# Basic liblzma-bindings for Ruby. -# -# Copyright © 2011-2018 Marvin Gülker et al. -# -# See AUTHORS for the full list of contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the ‘Software’), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the Software -# is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -#++ - -require_relative "common" - -class StreamReaderTest < Minitest::Test - - TEST_DATA_DIR = Pathname.new(__FILE__).dirname + "test-data" - PLAIN_TEXT_FILE = TEST_DATA_DIR + "lorem_ipsum.txt" - XZ_TEXT_FILE = TEST_DATA_DIR + "lorem_ipsum.txt.xz" - XZ_ISO_TEXT_FILE = TEST_DATA_DIR + "iso88591.txt.xz" - - def test_new - File.open(XZ_TEXT_FILE) do |file| - reader = XZ::StreamReader.new(file) - - assert_equal("Lorem ipsum", reader.read(11)) - assert_equal(" dolor sit amet", reader.read(15)) - - rest = reader.read - assert_equal("Lorem ipsum dolor sit amet.\n", rest[-28..-1]) - assert_equal("", reader.read) # We’re at EOF - assert(reader.eof?, "EOF is not EOF!") - - reader.close - end - end - - def test_file_closing - File.open(XZ_TEXT_FILE, "rb") do |file| - reader = XZ::StreamReader.new(file) - reader.read - reader.close - assert(file.closed?, "Did not close file although expected!") - end - - File.open(XZ_TEXT_FILE, "rb") do |file| - reader = XZ::StreamReader.new(file) - reader.read - reader.finish - assert(!file.closed?, "Closed file although not expected!") - end - - reader = XZ::StreamReader.open(XZ_TEXT_FILE){|r| r.read} - assert(reader.finished?, "Didn't finish stream!") - assert(reader.instance_variable_get(:@delegate_io).closed?, "Didn't close internally created file!") - - reader = XZ::StreamReader.open(XZ_TEXT_FILE) - reader.read - reader.close - assert(reader.instance_variable_get(:@delegate_io).closed?, "Didn't close internally created file!") - - reader = XZ::StreamReader.open(XZ_TEXT_FILE) - reader.read - reader.finish - assert(!reader.instance_variable_get(:@delegate_io).closed?, "Closed internally created file although not expected!") - - File.open(XZ_TEXT_FILE, "rb") do |file| - r = XZ::StreamReader.new(file) - r.read(10) - r.rewind - assert(!file.closed?, "Closed handed IO during rewind!") - end - - XZ::StreamReader.open(XZ_TEXT_FILE) do |r| - r.read(10) - r.rewind - assert(!r.instance_variable_get(:@delegate_io).closed?, "Closed internal file during rewind") - end - - # Test double closing (this should not raise) - XZ::StreamReader.open(XZ_TEXT_FILE) do |r| - r.close - end - - end - - def test_finish - File.open(XZ_TEXT_FILE, "rb") do |file| - r = XZ::StreamReader.new(file) - r.read - assert_equal file, r.finish - - assert r.finished?, "Didn't finish despite of #finish" - assert !file.closed?, "Closed wrapped file despite of #finish!" - end - - file = nil - XZ::StreamReader.open(XZ_TEXT_FILE){|r| r.read; file = r.finish} - assert_kind_of File, file # Return value of #finish - assert !file.closed?, "Closed wrapped file despite of #finish!" - file.close # cleanup - - reader = XZ::StreamReader.open(XZ_TEXT_FILE) - reader.read - file = reader.finish - assert_kind_of File, file - assert !file.closed?, "Closed wrapped file despite of #finish!" - file.close # cleanup - end - - def test_open - XZ::StreamReader.open(XZ_TEXT_FILE) do |reader| - assert_equal(File.read(PLAIN_TEXT_FILE), reader.read) - end - - File.open(XZ_TEXT_FILE, "rb") do |file| - reader = XZ::StreamReader.new(file) - assert_equal(File.read(PLAIN_TEXT_FILE), reader.read) - reader.close - end - end - - def test_pos - text = File.read(PLAIN_TEXT_FILE) - XZ::StreamReader.open(XZ_TEXT_FILE) do |reader| - reader.read - assert_equal(text.bytes.count, reader.pos) - end - end - - def test_rewind - # Non-block form - File.open(XZ_TEXT_FILE, "rb") do |file| - reader = XZ::StreamReader.new(file) - text = reader.read(10) - reader.rewind - assert_equal(text, reader.read(10)) - end - - # Block form - XZ::StreamReader.open(XZ_TEXT_FILE) do |reader| - text = reader.read(10) - reader.rewind - assert_equal(text, reader.read(10)) - end - end - - def test_encodings - enc1 = Encoding.default_external - enc2 = Encoding.default_internal - verb = $VERBOSE - $VERBOSE = nil # Disable warnings, because setting - # Encoding.default_{internal,external} generates a - # warning. However, setting these is required to test - # if they're properly honoured by ruby-xz. - begin - Encoding.default_external = Encoding::ISO_8859_1 - - # Forced binary read must always yield BINARY - XZ::StreamReader.open(XZ_ISO_TEXT_FILE) do |reader| - str = reader.read(255) - assert_equal Encoding::BINARY, str.encoding - end - - # Now the external encoding needs to be detected - XZ::StreamReader.open(XZ_ISO_TEXT_FILE) do |reader| - str = reader.read - assert_equal Encoding::ISO_8859_1, str.encoding - assert str.valid_encoding? - end - - # Request transcode - XZ::StreamReader.open(XZ_ISO_TEXT_FILE, external_encoding: "ISO-8859-1", internal_encoding: "UTF-8") do |reader| - str = reader.read - assert_equal Encoding::UTF_8, str.encoding - assert str.valid_encoding? - end - - # Request transcode via default internal encoding - Encoding.default_internal = Encoding::UTF_8 - XZ::StreamReader.open(XZ_ISO_TEXT_FILE) do |reader| - str = reader.read - assert_equal Encoding::UTF_8, str.encoding - assert str.valid_encoding? - end - - # Ensure getc does what it should when asked for multibyte chars - XZ::StreamReader.open(XZ_ISO_TEXT_FILE) do |reader| - assert_equal "B", reader.getc - assert_equal "ä", reader.getc - assert_equal "r", reader.getc - end - ensure - # Reset to normal for further tests - Encoding.default_external = enc1 - Encoding.default_internal = enc2 - $VERBOSE = verb - end - end - - def test_set_encoding - reader = XZ::StreamReader.open(XZ_ISO_TEXT_FILE) - - reader.set_encoding "UTF-8" - assert_equal Encoding::UTF_8, reader.external_encoding - assert_nil reader.internal_encoding - - reader.set_encoding "ISO-8859-1:UTF-8" - assert_equal Encoding::ISO_8859_1, reader.external_encoding - assert_equal Encoding::UTF_8, reader.internal_encoding - - reader.set_encoding Encoding::UTF_8 - assert_equal Encoding::UTF_8, reader.external_encoding - assert_nil reader.internal_encoding - - reader.set_encoding Encoding::UTF_8, Encoding::ISO_8859_1 - assert_equal Encoding::UTF_8, reader.external_encoding - assert_equal Encoding::ISO_8859_1, reader.internal_encoding - - reader.set_encoding "ISO-8859-1", {:invalid => :replace, :replace => "?"} - assert_equal Encoding::ISO_8859_1, reader.external_encoding - assert_nil reader.internal_encoding - - reader.set_encoding "ISO-8859-1", "UTF-8", {:invalid => :replace, :replace => "?"} - assert_equal Encoding::ISO_8859_1, reader.external_encoding - assert_equal Encoding::UTF_8, reader.internal_encoding - - reader.set_encoding "ISO-8859-1:UTF-8", {:invalid => :replace, :replace => "?"} - assert_equal Encoding::ISO_8859_1, reader.external_encoding - assert_equal Encoding::UTF_8, reader.internal_encoding - - end - -end diff --git a/test/test_stream_writer.rb b/test/test_stream_writer.rb deleted file mode 100644 index 91b76d8..0000000 --- a/test/test_stream_writer.rb +++ /dev/null @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- -#-- -# Basic liblzma-bindings for Ruby. -# -# Copyright © 2011-2018 Marvin Gülker et al. -# -# See AUTHORS for the full list of contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the ‘Software’), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the Software -# is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -#++ - -require_relative "common" - -# For this testcase, please note that it isn’t possible to check -# whether the compressed string is equal to some other -# compressed string containing the same original text due to -# different compression options and/or different versions of -# liblzma. Hence, I can only test whether the re-decompressed -# result is equal to what I originally had. -class StreamWriterTest < Minitest::Test - - TEST_DATA_DIR = Pathname.new(__FILE__).dirname + "test-data" - PLAIN_TEXT_FILE = TEST_DATA_DIR + "lorem_ipsum.txt" - XZ_TEXT_FILE = TEST_DATA_DIR + "lorem_ipsum.txt.xz" - LIVE_TEST_FILE = TEST_DATA_DIR + "lorem2.txt.xz" - - def test_stream_writer_new - text = File.read(PLAIN_TEXT_FILE) - text1 = text[0...10] - text2 = text[10..-1] - - File.open(LIVE_TEST_FILE, "wb") do |file| - writer = XZ::StreamWriter.new(file) - - assert_equal(text1.bytes.count, writer.write(text1)) - assert_equal(text2.bytes.count, writer.write(text2)) - writer.close - - assert(text.bytesize > File.stat(LIVE_TEST_FILE).size, "Compression did not compress") - assert(writer.finished?, "Didn't finish writer") - assert(writer.closed?, "Didn't close writer") - assert_raises(IOError){writer.write("foo")} - end - - assert_equal(text, XZ.decompress(File.open(LIVE_TEST_FILE, "rb"){|f| f.read})) - end - - def test_file_closing - File.open(LIVE_TEST_FILE, "wb") do |file| - w = XZ::StreamWriter.new(file) - w.write("Foo") - w.finish - assert(!file.closed?, "Closed file although not expected!") - end - - File.open(LIVE_TEST_FILE, "wb") do |file| - w = XZ::StreamWriter.new(file) - w.write("Foo") - w.close - assert(w.finished?, "Didn't finish writer although expected!") - assert(file.closed?, "Didn't close file although expected!") - end - - writer = XZ::StreamWriter.open(LIVE_TEST_FILE){|w| w.write("Foo")} - assert(writer.finished?, "Didn't finish writer") - assert(writer.instance_variable_get(:@delegate_io).closed?, "Didn't close internally opened file!") - - writer = XZ::StreamWriter.new(File.open(LIVE_TEST_FILE, "wb")) - writer.write("Foo") - writer.close - assert(writer.finished?, "Didn't finish writer") - assert(writer.instance_variable_get(:@delegate_io).closed?, "Didn't close internally opened file!") - - # Test double closing (this should not raise) - XZ::StreamWriter.open(LIVE_TEST_FILE) do |w| - w.write("Foo") - w.close - end - end - - def test_finish - File.open(LIVE_TEST_FILE, "wb") do |file| - w = XZ::StreamWriter.new(file) - w.write("Foo") - - assert_equal file, w.finish - assert !file.closed?, "Closed wrapped file despite of #finish!" - end - - file = nil - XZ::StreamWriter.open(LIVE_TEST_FILE){|w| w.write("Foo"); file = w.finish} - assert_kind_of File, file # Return value of #finish - assert !file.closed?, "Closed wrapped file despite of #finish!" - file.close # cleanup - - writer = XZ::StreamWriter.open(LIVE_TEST_FILE) - writer.write("Foo") - file = writer.finish - assert_kind_of File, file - assert !file.closed?, "Closed wrapped file despite of #finish!" - file.close # cleanup - end - - def test_stream_writer_open - text = File.read(PLAIN_TEXT_FILE) - - XZ::StreamWriter.open(LIVE_TEST_FILE) do |file| - file.write(text) - end - - assert_equal(text, XZ.decompress(File.open(LIVE_TEST_FILE, "rb"){|f| f.read})) - end - -end diff --git a/test/test_xz.rb b/test/test_xz.rb deleted file mode 100755 index ede1575..0000000 --- a/test/test_xz.rb +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -#-- -# Basic liblzma-bindings for Ruby. -# -# Copyright © 2011-2018 Marvin Gülker et al. -# -# See AUTHORS for the full list of contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the ‘Software’), -# to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the Software -# is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -#++ - -require_relative "common" - -class TestXZ < Minitest::Test - - TEST_XZ = "\3757zXZ\000\000\004\346\326\264F\002\000!\001\026\000\000\000t/" + - "\345\243\340\000\023\000\020]\000\030\fB\222jg\274\016\32132a\326|\000\000" + - "\000\017:\376\373\"1\270\266\000\001,\024\370\nm\003\037\266\363}\001\000" + - "\000\000\000\004YZ" - - def test_decompress - assert_equal(XZ.decompress(TEST_XZ), '01234567890123456789') - assert_equal(Encoding.default_external, XZ.decompress(TEST_XZ).encoding) - - str = XZ.decompress(TEST_XZ, external_encoding: "ISO-8859-1") - assert_equal Encoding::ISO_8859_1, str.encoding - - str = XZ.decompress(TEST_XZ, external_encoding: "ISO-8859-1", internal_encoding: "UTF-16LE") - assert_equal Encoding::UTF_16LE, str.encoding - - # Must specify an external encoding if transcoding is requested - assert_raises(ArgumentError){XZ.decompress(TEST_XZ, internal_encoding: "UTF-8")} - end - - def test_corrupt_archive - corrupt_xz = TEST_XZ.dup - corrupt_xz[20] = "\023" - assert_raises(XZ::LZMAError) { XZ.decompress(corrupt_xz) } - end - - def test_compress - str = '01234567890123456789' - tmp = XZ.compress(str) - assert_equal(tmp[0, 5].bytes.to_a, "\3757zXZ".bytes.to_a) - - # Ensure it interacts with upstream xz properly - IO.popen("xzcat", "w+") do |io| - io.write(tmp) - io.close_write - assert_equal(io.read, str) - end - end - - def test_compression_levels - str = "Once upon a time, there was..." - - 0.upto(9) do |i| - [true, false].each do |extreme| - compressed = XZ.compress(str, level: i, extreme: extreme) - assert_equal(XZ.decompress(compressed), str) - end - end - - # Maximum compression level is 9. - assert_raises(ArgumentError){XZ.compress("foo", level: 15)} - end - - def test_roundtrip - str = "Once upon a time, there was..." - assert_equal(XZ.decompress(XZ.compress(str)), str) - end - - def test_compress_file - Tempfile.open('in') do |infile| - infile.write('01234567890123456789') - infile.close - - Tempfile.open('out') do |outfile| - outfile.close - - XZ.compress_file(infile.path, outfile.path) - - outfile.open - assert_equal(outfile.read[0, 5].bytes.to_a, "\3757zXZ".bytes.to_a) - end - end - end - - def test_decompress_file - Tempfile.open('in') do |infile| - infile.write(TEST_XZ) - infile.close - - Tempfile.open('out') do |outfile| - outfile.close - - XZ.decompress_file(infile.path, outfile.path) - - outfile.open - assert_equal(outfile.read, '01234567890123456789') - end - end - end - -end - From 8e1d1157a95ed3c0a7d1ad9d32e3d773568bd431 Mon Sep 17 00:00:00 2001 From: Alex Gittemeier Date: Wed, 6 Jul 2022 00:23:29 -0500 Subject: [PATCH 2/5] Some changes are required to continue to support ruby:2.3 in CI --- .gitlab-ci.yml | 16 ++++++++++++---- Gemfile.lock | 8 ++++---- ruby-xz.gemspec | 8 ++++---- spec/spec_helper.rb | 3 ++- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4c71947..f5d570d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,12 @@ before_script: - rm -rf /var/cache/apk - ln -s "$PWD"/vendor/apk /var/cache/ - apk add git xz + - if [ "$RUBY_IS_EOL" ]; then + echo "[!] Turning off pins in Gemfile.lock. Here be dragons!" && + export BUNDLE_DEPLOYMENT=false && + rm Gemfile.lock && + gem update --system 3.2.3; + fi - gem install bundler -v 2.3.7 - bundle install || (apk add gcc g++ make && bundle install) cache: @@ -45,23 +51,25 @@ test ruby-2.6: test ruby-2.5: image: ruby:2.5-alpine stage: test + variables: + RUBY_IS_EOL: since 2021-04-05 script: - - gem update --system 3.2.3 - bundle exec rake test ruby-2.4: image: ruby:2.4-alpine stage: test + variables: + RUBY_IS_EOL: since 2020-03-31 script: - - gem update --system 3.2.3 - bundle exec rake - test ruby-2.3: image: ruby:2.3-alpine stage: test + variables: + RUBY_IS_EOL: since 2019-03-31 script: - - gem update --system 3.2.3 - bundle exec rake check for bundle updates: diff --git a/Gemfile.lock b/Gemfile.lock index 01a83ee..9f243b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,11 +64,11 @@ DEPENDENCIES minitar (~> 0.6) rake (~> 13.0) rspec (~> 3.11) - rubocop (~> 1.31) - rubocop-rake (~> 0.6.0) - rubocop-rspec (~> 2.12) + rubocop + rubocop-rake + rubocop-rspec ruby-xz! - simplecov (~> 0.21.2) + simplecov (~> 0.17) BUNDLED WITH 2.3.7 diff --git a/ruby-xz.gemspec b/ruby-xz.gemspec index 4c0b8ab..90a3646 100644 --- a/ruby-xz.gemspec +++ b/ruby-xz.gemspec @@ -62,8 +62,8 @@ GEMSPEC = Gem::Specification.new do |spec| spec.add_development_dependency 'minitar', '~> 0.6' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rspec', '~> 3.11' - spec.add_development_dependency 'rubocop', '~> 1.31' - spec.add_development_dependency 'rubocop-rake', '~> 0.6.0' - spec.add_development_dependency 'rubocop-rspec', '~> 2.12' - spec.add_development_dependency 'simplecov', '~> 0.21.2' + spec.add_development_dependency 'rubocop' + spec.add_development_dependency 'rubocop-rake' + spec.add_development_dependency 'rubocop-rspec' + spec.add_development_dependency 'simplecov', '~> 0.17' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ad6e653..867617d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -29,7 +29,8 @@ require 'simplecov' SimpleCov.start do - enable_coverage :branch + enable_coverage :branch if respond_to?(:enable_coverage) # XXX: ruby 2.3 compatibility + coverage_dir File.join(__dir__, 'coverage') end From 5e6c747a1c9af908c06e80213aa12b14aef9a130 Mon Sep 17 00:00:00 2001 From: Alex Gittemeier Date: Wed, 6 Jul 2022 01:02:37 -0500 Subject: [PATCH 3/5] Update bundler and test for ruby:3.2-rc --- .gitlab-ci.yml | 8 +++++++- Gemfile.lock | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5d570d..fce1613 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ before_script: rm Gemfile.lock && gem update --system 3.2.3; fi - - gem install bundler -v 2.3.7 + - gem install bundler -v 2.3.17 - bundle install || (apk add gcc g++ make && bundle install) cache: key: global-dependency-cache @@ -24,6 +24,12 @@ cache: stages: - test +test ruby-3.2-rc: + image: ruby:3.2-rc-alpine + stage: test + script: + - bundle exec rake + test ruby-3.1: image: ruby:3.1-alpine stage: test diff --git a/Gemfile.lock b/Gemfile.lock index 9f243b0..e42e10f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,4 +71,4 @@ DEPENDENCIES simplecov (~> 0.17) BUNDLED WITH - 2.3.7 + 2.3.17 From e1ce6648ed57f8217a972846e9bc20bdb9c13655 Mon Sep 17 00:00:00 2001 From: Alex Gittemeier Date: Wed, 6 Jul 2022 01:20:42 -0500 Subject: [PATCH 4/5] Use ruby2.3 compatible API in test suite --- spec/minitar/integration_spec.rb | 7 +++++-- spec/xz_spec.rb | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/minitar/integration_spec.rb b/spec/minitar/integration_spec.rb index c419ad6..1305939 100644 --- a/spec/minitar/integration_spec.rb +++ b/spec/minitar/integration_spec.rb @@ -26,6 +26,7 @@ require 'xz' +require 'English' require 'minitar' require 'pathname' @@ -45,7 +46,8 @@ Dir.mktmpdir('testtarball') do |dir| Dir.chdir(dir) do - system("tar -xJf '#{filename}'", exception: true) + system("tar -xJf '#{filename}'") + expect($CHILD_STATUS).to be_success # TODO: in ruby 2.7+, the line above can have `exception: true` expect(Pathname('test-data/lorem_ipsum.txt')).to exist expect(File.read('test-data/lorem_ipsum.txt')).to eq(content) end @@ -57,7 +59,8 @@ content = File.read(TestData::PLAINTEXT_FILENAME) Dir.chdir(TestData::PARENT_DIRNAME) do # proper file path parts - system("tar -cJf '#{filename}' test-data/lorem_ipsum.txt", exception: true) + system("tar -cJf '#{filename}' test-data/lorem_ipsum.txt") + expect($CHILD_STATUS).to be_success # TODO: in ruby 2.7+, the line above can have `exception: true` end Dir.mktmpdir('testtarball') do |dir| diff --git a/spec/xz_spec.rb b/spec/xz_spec.rb index 3e69c30..0476a68 100755 --- a/spec/xz_spec.rb +++ b/spec/xz_spec.rb @@ -26,6 +26,7 @@ require 'xz' +require 'English' require 'tempfile' RSpec.describe XZ do @@ -79,6 +80,7 @@ io.close_write expect(io.read).to eq(str) end + expect($CHILD_STATUS).to be_success # TODO: in ruby 2.5+, we can test Process.last_status instead end it 'accepts compression levels 0 upto 9, with and without extreme option' do From 4bf084bbfa1e589f7d017b57a3134934728f4db1 Mon Sep 17 00:00:00 2001 From: Alex Gittemeier Date: Wed, 6 Jul 2022 02:16:13 -0500 Subject: [PATCH 5/5] Adjust rubocop settings --- .rubocop.yml | 14 +++++--------- lib/xz/version.rb | 5 +++-- spec/xz/stream_reader_spec.rb | 2 +- spec/xz/stream_writer_spec.rb | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3ac6b84..2c00e5d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,11 +3,6 @@ require: rubocop-rspec AllCops: TargetRubyVersion: 2.3.0 NewCops: enable - Exclude: - - 'node_modules/**/*' - - 'tmp/**/*' - - 'vendor/**/*' - - '.git/**/*' Layout/ArgumentAlignment: EnforcedStyle: with_fixed_indentation @@ -39,9 +34,6 @@ Metrics/BlockLength: IgnoredMethods: - describe -Naming/MethodParameterName: - AllowedNames: [x, y, at, by, id, in, of, on, to] - RSpec/MultipleExpectations: Enabled: false RSpec/ExampleLength: @@ -61,7 +53,11 @@ Style/NumericPredicate: EnforcedStyle: comparison Style/SymbolArray: EnforcedStyle: brackets +Style/TrailingCommaInArguments: + EnforcedStyleForMultiline: comma Style/TrailingCommaInArrayLiteral: - Enabled: false + EnforcedStyleForMultiline: comma +Style/TrailingCommaInHashLiteral: + EnforcedStyleForMultiline: comma Style/WordArray: MinSize: 4 diff --git a/lib/xz/version.rb b/lib/xz/version.rb index 65b763a..7b1d2ce 100644 --- a/lib/xz/version.rb +++ b/lib/xz/version.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -# -*- coding: utf-8 -*- + #-- # Basic liblzma-bindings for Ruby. # -# Copyright © 2011-2018 Marvin Gülker et al. +# Copyright © 2011-2022 Marvin Gülker et al. # # See AUTHORS for the full list of contributors. # @@ -26,6 +26,7 @@ # THE SOFTWARE. #++ + module XZ # The version of this library. VERSION = '1.0.4.pre' diff --git a/spec/xz/stream_reader_spec.rb b/spec/xz/stream_reader_spec.rb index 985a52d..3e43a8c 100644 --- a/spec/xz/stream_reader_spec.rb +++ b/spec/xz/stream_reader_spec.rb @@ -65,7 +65,7 @@ end it 'finishes the stream and closes the underlying file at the end of the block (in block form)' do - #XXX: surprising API: I would have expected the method to passthru the block value + # XXX: surprising API: I would have expected the method to passthru the block value reader = XZ::StreamReader.open(TestData::PLAINTEXT_XZ_FILENAME, &:read) expect(reader).to be_finished expect(reader.instance_variable_get(:@delegate_io)).to be_closed diff --git a/spec/xz/stream_writer_spec.rb b/spec/xz/stream_writer_spec.rb index b7e2599..31ed365 100644 --- a/spec/xz/stream_writer_spec.rb +++ b/spec/xz/stream_writer_spec.rb @@ -76,7 +76,7 @@ end it 'finishes the stream and closes the underlying file at the end of the block (in block form)' do - #XXX: surprising API: I would have expected the method to passthru the block value + # XXX: surprising API: I would have expected the method to passthru the block value writer = XZ::StreamWriter.open(TestData::LIVE_TEST_FILENAME) {|w| w.write('Foo') } expect(writer).to be_finished expect(writer.instance_variable_get(:@delegate_io)).to be_closed