diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml new file mode 100644 index 0000000..6188c62 --- /dev/null +++ b/.github/workflows/valgrind.yml @@ -0,0 +1,39 @@ +name: Valgrind + +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + valgrind: + name: memcheck (ubuntu, ruby ${{ matrix.ruby }}) + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + ruby: + - '4.0' + + steps: + - uses: actions/checkout@v6 + - name: Install Valgrind + run: | + sudo apt-get update + sudo apt-get install -y valgrind + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run the spec suite under Valgrind memcheck + run: bundle exec rake spec:valgrind diff --git a/Gemfile b/Gemfile index afffcc2..07ae089 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,7 @@ source 'https://rubygems.org' # Specify your gem's dependencies in zstd_ruby.gemspec gemspec + +if RUBY_PLATFORM.include?('linux') && Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') + gem 'ruby_memcheck', '~> 3.0' +end diff --git a/Rakefile b/Rakefile index f76e1c2..6a89482 100644 --- a/Rakefile +++ b/Rakefile @@ -15,6 +15,32 @@ end task :default => [:clobber, :compile, :spec] +begin + require 'ruby_memcheck' + require 'ruby_memcheck/rspec/rake_task' + + RubyMemcheck.config( + binary_name: 'zstdruby', + # Valgrind and YJIT interfere with each other, adding noise and slowdown, + # so keep YJIT disabled while running under Valgrind. + ruby: "#{FileUtils::RUBY} --disable-yjit" + ) + + namespace :spec do + task :check_valgrind do + unless system('command -v valgrind > /dev/null 2>&1') + abort("\nValgrind is required for `rake spec:valgrind` but was not found.\n" \ + "Install it first (Linux only), e.g. `sudo apt-get install valgrind`.\n") + end + end + + RubyMemcheck::RSpec::RakeTask.new(valgrind: [:check_valgrind, :compile]) + end +rescue LoadError + # ruby_memcheck is an optional development dependency, absent on the platforms + # and Ruby versions the Gemfile excludes. Skip the task instead of breaking. +end + desc 'Sync zstd libs dirs to ext/zstdruby/libzstd' task :zstd_update do FileUtils.rm_r("ext/zstdruby/libzstd") diff --git a/spec/zstd-ruby_spec.rb b/spec/zstd-ruby_spec.rb index 6ef02d9..f5a373a 100644 --- a/spec/zstd-ruby_spec.rb +++ b/spec/zstd-ruby_spec.rb @@ -34,7 +34,7 @@ end it 'should compress large bytes' do - large_string = Random.bytes(1<<17 + 15) + large_string = Random.bytes((1<<17) + 15) compressed = Zstd.compress(large_string) expect(Zstd.decompress(compressed)).to eq(large_string) end