File: test_gem_compatibility.rb

package info (click to toggle)
ruby-dalli 5.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 992 kB
  • sloc: ruby: 9,447; sh: 19; makefile: 4
file content (97 lines) | stat: -rw-r--r-- 2,720 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# frozen_string_literal: true

# JRuby does not support forking, so skip these tests on JRuby.
return unless Process.respond_to?(:fork)

require_relative 'helper'

# Gems that monkey-patch TCPSocket and may break connect_timeout: keyword argument.
# These are loaded in isolated forked processes to avoid affecting other tests.
# Format: { gem_name => require_path }
GEM_COMPATIBILITY_TEST_GEMS = {
  'resolv-replace' => 'resolv-replace',
  'socksify' => 'socksify'
}.freeze

describe 'gem compatibility' do
  GEM_COMPATIBILITY_TEST_GEMS.each do |gem_name, require_path|
    describe "with #{gem_name} gem" do
      it 'can perform basic operations after gem is required' do
        memcached(:meta, rand(21_397..21_896)) do |_, port|
          run_compatibility_test(gem_name, require_path, port)
        end
      end
    end
  end

  private

  def run_compatibility_test(gem_name, require_path, port)
    read_pipe, write_pipe = IO.pipe

    pid = fork do
      read_pipe.close
      execute_compatibility_test(gem_name, require_path, port, write_pipe)
    end

    write_pipe.close
    wait_for_child(pid, read_pipe, timeout: 15)
  end

  def execute_compatibility_test(gem_name, require_path, port, write_pipe)
    # Verify operations work before requiring the gem
    before_client = Dalli::Client.new("127.0.0.1:#{port}")

    assert_round_trip(before_client, "before requiring #{gem_name}")

    # Require the gem (this may monkey-patch TCPSocket)
    require require_path

    # Verify operations still work after requiring the gem
    after_client = Dalli::Client.new("127.0.0.1:#{port}")

    assert_round_trip(after_client, "after requiring #{gem_name}")

    write_pipe.write('OK')
  rescue Exception => e # rubocop:disable Lint/RescueException
    write_pipe.write(Marshal.dump(e))
  ensure
    write_pipe.close
    exit!(0)
  end

  def wait_for_child(pid, read_pipe, timeout:)
    Timeout.timeout(timeout) do
      Process.wait(pid)
      handle_child_result(read_pipe.read)
    end
  rescue Timeout::Error
    Process.kill('KILL', pid)
    Process.wait(pid)

    flunk "Child process timed out after #{timeout}s"
  ensure
    read_pipe.close
  end

  def handle_child_result(result)
    if result == 'OK'
      pass
    elsif result.empty?
      flunk "Child process exited without result (status: #{$CHILD_STATUS.exitstatus})"
    else
      raise Marshal.load(result) # rubocop:disable Security/MarshalLoad
    end
  end

  def assert_round_trip(client, context)
    key = "compat-test-#{SecureRandom.hex(4)}"
    expected = SecureRandom.hex(8)
    ttl = 60

    client.set(key, expected, ttl)
    actual = client.get(key)

    assert_equal expected, actual, "Round-trip failed #{context}"
  end
end