-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PROF-10241] Fix issue in tests by simplifying at_fork
monkey patch
#3834
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6,15 +6,15 @@ | |||||||||||||||||
describe '::apply!' do | ||||||||||||||||||
subject(:apply!) { described_class.apply! } | ||||||||||||||||||
|
||||||||||||||||||
let(:toplevel_receiver) { TOPLEVEL_BINDING.receiver } | ||||||||||||||||||
|
||||||||||||||||||
context 'when forking is supported' do | ||||||||||||||||||
before do | ||||||||||||||||||
if ::Process.singleton_class.ancestors.include?(Datadog::Core::Utils::AtForkMonkeyPatch::ProcessMonkeyPatch) | ||||||||||||||||||
skip 'Monkey patch already applied (unclean state)' | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
let(:toplevel_receiver) { TOPLEVEL_BINDING.receiver } | ||||||||||||||||||
|
||||||||||||||||||
context 'on Ruby 3.0 or below' do | ||||||||||||||||||
before { skip 'Test applies only to Ruby 3.0 or below' if RUBY_VERSION >= '3.1' } | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -47,6 +47,20 @@ | |||||||||||||||||
expect(::Process.method(:_fork).source_location.first).to match(/.*at_fork_monkey_patch.rb/) | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'does not monkey patch Kernel/Object' do | ||||||||||||||||||
expect_in_fork do | ||||||||||||||||||
apply! | ||||||||||||||||||
|
||||||||||||||||||
expect(::Process.ancestors).to_not include(described_class::KernelMonkeyPatch) | ||||||||||||||||||
expect(::Kernel.ancestors).to_not include(described_class::KernelMonkeyPatch) | ||||||||||||||||||
expect(toplevel_receiver.class.ancestors).to_not include(described_class::KernelMonkeyPatch) | ||||||||||||||||||
|
||||||||||||||||||
expect(::Process.method(:fork).source_location&.first).to_not match(/.*at_fork_monkey_patch.rb/) | ||||||||||||||||||
expect(::Kernel.method(:fork).source_location&.first).to_not match(/.*at_fork_monkey_patch.rb/) | ||||||||||||||||||
expect(toplevel_receiver.method(:fork).source_location&.first).to_not match(/.*at_fork_monkey_patch.rb/) | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -57,7 +71,7 @@ | |||||||||||||||||
.and_return(false) | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'skips the Kernel patch' do | ||||||||||||||||||
it 'skips the monkey patch' do | ||||||||||||||||||
is_expected.to be false | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
@@ -91,15 +105,15 @@ def fork(&block) | |||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
shared_context 'datadog_at_fork callbacks' do | ||||||||||||||||||
shared_context 'at_fork callbacks' do | ||||||||||||||||||
let(:child) { double('child') } | ||||||||||||||||||
|
||||||||||||||||||
before do | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch::ProcessMonkeyPatch.datadog_at_fork(:child) { child.call } | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch.at_fork(:child) { child.call } | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
after do | ||||||||||||||||||
described_class.datadog_at_fork_blocks.clear | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch.const_get(:AT_FORK_CHILD_BLOCKS).clear | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -112,14 +126,12 @@ def fork(&block) | |||||||||||||||||
|
||||||||||||||||||
describe '#fork' do | ||||||||||||||||||
context 'when a block is not provided' do | ||||||||||||||||||
include_context 'datadog_at_fork callbacks' | ||||||||||||||||||
include_context 'at_fork callbacks' | ||||||||||||||||||
|
||||||||||||||||||
subject(:fork) { fork_class.fork } | ||||||||||||||||||
|
||||||||||||||||||
context 'and returns from the parent context' do | ||||||||||||||||||
# By setting the fork result = integer, we're | ||||||||||||||||||
# simulating #fork running in the parent process. | ||||||||||||||||||
let(:fork_result) { rand(100) } | ||||||||||||||||||
let(:fork_result) { 1234 } # simulate parent: result is a pid | ||||||||||||||||||
|
||||||||||||||||||
it do | ||||||||||||||||||
expect(child).to_not receive(:call) | ||||||||||||||||||
|
@@ -129,9 +141,7 @@ def fork(&block) | |||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
context 'and returns from the child context' do | ||||||||||||||||||
# By setting the fork result = nil, we're | ||||||||||||||||||
# simulating #fork running in the child process. | ||||||||||||||||||
let(:fork_result) { nil } | ||||||||||||||||||
let(:fork_result) { nil } # simulate child: result is a nil | ||||||||||||||||||
|
||||||||||||||||||
it do | ||||||||||||||||||
expect(child).to receive(:call) | ||||||||||||||||||
|
@@ -154,7 +164,7 @@ def fork(&block) | |||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
context 'when callbacks are configured' do | ||||||||||||||||||
include_context 'datadog_at_fork callbacks' | ||||||||||||||||||
include_context 'at_fork callbacks' | ||||||||||||||||||
|
||||||||||||||||||
it 'invokes all the callbacks in order' do | ||||||||||||||||||
expect(child).to receive(:call) | ||||||||||||||||||
|
@@ -164,48 +174,6 @@ def fork(&block) | |||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
describe '#datadog_at_fork' do | ||||||||||||||||||
include_context 'datadog_at_fork callbacks' | ||||||||||||||||||
|
||||||||||||||||||
let(:callback) { double('callback') } | ||||||||||||||||||
let(:block) { proc { callback.call } } | ||||||||||||||||||
|
||||||||||||||||||
subject(:datadog_at_fork) do | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch::ProcessMonkeyPatch.datadog_at_fork(:child, &block) | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'adds a child callback' do | ||||||||||||||||||
datadog_at_fork | ||||||||||||||||||
|
||||||||||||||||||
expect(child).to receive(:call).ordered | ||||||||||||||||||
expect(callback).to receive(:call).ordered | ||||||||||||||||||
|
||||||||||||||||||
fork_class.fork {} | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
context 'when applied to multiple classes with forking' do | ||||||||||||||||||
include_context 'fork class' | ||||||||||||||||||
|
||||||||||||||||||
let(:other_fork_class) { new_fork_class } | ||||||||||||||||||
|
||||||||||||||||||
context 'and #datadog_at_fork is called in one' do | ||||||||||||||||||
include_context 'datadog_at_fork callbacks' | ||||||||||||||||||
|
||||||||||||||||||
it 'applies the callback to the original class' do | ||||||||||||||||||
expect(child).to receive(:call) | ||||||||||||||||||
|
||||||||||||||||||
fork_class.fork {} | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'applies the callback to the other class' do | ||||||||||||||||||
expect(child).to receive(:call) | ||||||||||||||||||
|
||||||||||||||||||
other_fork_class.fork {} | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
|
@@ -215,49 +183,42 @@ def fork(&block) | |||||||||||||||||
result = _fork_result | ||||||||||||||||||
|
||||||||||||||||||
Module.new do | ||||||||||||||||||
def self.daemon(nochdir = nil, noclose = nil); end | ||||||||||||||||||
def self.daemon(nochdir = nil, noclose = nil) | ||||||||||||||||||
[nochdir, noclose] | ||||||||||||||||||
end | ||||||||||||||||||
define_singleton_method(:_fork) { result } | ||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
let(:child_callback) { double('child', call: true) } | ||||||||||||||||||
|
||||||||||||||||||
before do | ||||||||||||||||||
allow(process_module).to receive(:daemon) | ||||||||||||||||||
|
||||||||||||||||||
process_module.singleton_class.prepend(Datadog::Core::Utils::AtForkMonkeyPatch::KernelMonkeyPatch) | ||||||||||||||||||
process_module.singleton_class.prepend(described_class) | ||||||||||||||||||
|
||||||||||||||||||
process_module.datadog_at_fork(:child) { child_callback.call } | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch.at_fork(:child) { child_callback.call } | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
after do | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch::KernelMonkeyPatch.datadog_at_fork_blocks.clear | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'calls the child datadog_at_fork callbacks after calling Process.daemon' do | ||||||||||||||||||
expect(process_module).to receive(:daemon).ordered | ||||||||||||||||||
expect(child_callback).to receive(:call).ordered | ||||||||||||||||||
|
||||||||||||||||||
process_module.daemon | ||||||||||||||||||
Datadog::Core::Utils::AtForkMonkeyPatch.const_get(:AT_FORK_CHILD_BLOCKS).clear | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'passes any arguments to Process.daemon' do | ||||||||||||||||||
expect(process_module).to receive(:daemon).with(true, true) | ||||||||||||||||||
describe '.daemon' do | ||||||||||||||||||
it 'calls the child at_fork callbacks after calling Process.daemon' do | ||||||||||||||||||
expect(process_module).to receive(:daemon).ordered.and_call_original | ||||||||||||||||||
expect(child_callback).to receive(:call).ordered | ||||||||||||||||||
|
||||||||||||||||||
process_module.daemon(true, true) | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
it 'returns the result of calling Process.daemon' do | ||||||||||||||||||
expect(process_module).to receive(:daemon).and_return(:process_daemon_result) | ||||||||||||||||||
process_module.daemon | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
expect(process_module.daemon).to be :process_daemon_result | ||||||||||||||||||
it 'passes any arguments to Process.daemon and returns its results' do | ||||||||||||||||||
expect(process_module.daemon(:arg1, :arg2)).to eq([:arg1, :arg2]) | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ Code Quality Violation
Suggested change
Consider using the %i syntax instead (...read more)The rule "Prefer Symbols are immutable, reusable objects often used in Ruby instead of strings when the value does not need to be changed. When declaring an array of symbols, using the To adhere to this rule, instead of declaring an array of symbols using the literal array syntax like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ Code Quality Violation
Suggested change
Consider using the %i syntax instead (...read more)The rule "Prefer Symbols are immutable, reusable objects often used in Ruby instead of strings when the value does not need to be changed. When declaring an array of symbols, using the To adhere to this rule, instead of declaring an array of symbols using the literal array syntax like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ Code Quality Violation
Suggested change
Consider using the %i syntax instead (...read more)The rule "Prefer Symbols are immutable, reusable objects often used in Ruby instead of strings when the value does not need to be changed. When declaring an array of symbols, using the To adhere to this rule, instead of declaring an array of symbols using the literal array syntax like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ Code Quality Violation
Suggested change
Consider using the %i syntax instead (...read more)The rule "Prefer Symbols are immutable, reusable objects often used in Ruby instead of strings when the value does not need to be changed. When declaring an array of symbols, using the To adhere to this rule, instead of declaring an array of symbols using the literal array syntax like |
||||||||||||||||||
end | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
describe 'Process._fork monkey patch' do | ||||||||||||||||||
describe '_fork' do | ||||||||||||||||||
context 'in the child process' do | ||||||||||||||||||
let(:_fork_result) { 0 } | ||||||||||||||||||
|
||||||||||||||||||
it 'monkey patches _fork to call the child datadog_at_fork callbacks on the child process' do | ||||||||||||||||||
it 'triggers the child callbacks' do | ||||||||||||||||||
expect(child_callback).to receive(:call) | ||||||||||||||||||
|
||||||||||||||||||
expect(process_module._fork).to be 0 | ||||||||||||||||||
|
@@ -271,7 +232,7 @@ def self.daemon(nochdir = nil, noclose = nil); end | |||||||||||||||||
context 'in the parent process' do | ||||||||||||||||||
let(:_fork_result) { 1234 } | ||||||||||||||||||
|
||||||||||||||||||
it 'does not trigger any callbacks' do | ||||||||||||||||||
it 'does not trigger the child callbacks' do | ||||||||||||||||||
expect(child_callback).to_not receive(:call) | ||||||||||||||||||
|
||||||||||||||||||
process_module._fork | ||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚪ Code Quality Violation
Consider using the %i syntax instead (...read more)
The rule "Prefer
%i
to the literal array syntax" is a guideline that encourages the use of the%i
syntax for arrays of symbols. This is a part of the Ruby style guide that aims to promote conciseness and readability.Symbols are immutable, reusable objects often used in Ruby instead of strings when the value does not need to be changed. When declaring an array of symbols, using the
%i
syntax can make your code cleaner and easier to read.To adhere to this rule, instead of declaring an array of symbols using the literal array syntax like
[:foo, :bar, :baz]
, use the%i
syntax like%i[foo bar baz]
. It's a good practice to consistently use%i
for arrays of symbols as it enhances code readability and maintainability.