This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ethon::Multi::Operations do
|
||||
let(:multi) { Ethon::Multi.new }
|
||||
let(:easy) { Ethon::Easy.new }
|
||||
let(:pointer) { FFI::MemoryPointer.new(:int) }
|
||||
|
||||
describe "#handle" do
|
||||
it "returns a pointer" do
|
||||
expect(multi.handle).to be_a(FFI::Pointer)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#running_count" do
|
||||
context "when hydra has no easy" do
|
||||
it "returns nil" do
|
||||
expect(multi.send(:running_count)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when hydra has easy" do
|
||||
before do
|
||||
easy.url = "http://localhost:3001/"
|
||||
multi.add(easy)
|
||||
multi.send(:trigger, pointer)
|
||||
end
|
||||
|
||||
it "returns 1" do
|
||||
expect(multi.send(:running_count)).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hydra has more easys" do
|
||||
let(:another_easy) { Ethon::Easy.new }
|
||||
|
||||
before do
|
||||
easy.url = "http://localhost:3001/"
|
||||
another_easy.url = "http://localhost:3001/"
|
||||
multi.add(easy)
|
||||
multi.add(another_easy)
|
||||
multi.send(:trigger, pointer)
|
||||
end
|
||||
|
||||
it "returns 2" do
|
||||
expect(multi.send(:running_count)).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#get_timeout" do
|
||||
context "when code ok" do
|
||||
let(:timeout) { 1 }
|
||||
|
||||
before do
|
||||
expect(Ethon::Curl).to receive(:multi_timeout).and_return(:ok)
|
||||
multi.instance_variable_set(:@timeout, double(:read_long => timeout))
|
||||
end
|
||||
|
||||
it "doesn't raise" do
|
||||
expect{ multi.send(:get_timeout) }.to_not raise_error
|
||||
end
|
||||
|
||||
context "when timeout smaller zero" do
|
||||
let(:timeout) { -1 }
|
||||
|
||||
it "returns 1" do
|
||||
expect(multi.send(:get_timeout)).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when timeout bigger or equal zero" do
|
||||
let(:timeout) { 2 }
|
||||
|
||||
it "returns timeout" do
|
||||
expect(multi.send(:get_timeout)).to eq(timeout)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when code not ok" do
|
||||
before { expect(Ethon::Curl).to receive(:multi_timeout).and_return(:not_ok) }
|
||||
|
||||
it "raises MultiTimeout error" do
|
||||
expect{ multi.send(:get_timeout) }.to raise_error(Ethon::Errors::MultiTimeout)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#set_fds" do
|
||||
let(:timeout) { 1 }
|
||||
let(:max_fd) { 1 }
|
||||
|
||||
context "when code ok" do
|
||||
before { expect(Ethon::Curl).to receive(:multi_fdset).and_return(:ok) }
|
||||
|
||||
it "doesn't raise" do
|
||||
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error
|
||||
end
|
||||
|
||||
context "when max_fd -1" do
|
||||
let(:max_fd) { -1 }
|
||||
|
||||
before do
|
||||
multi.instance_variable_set(:@max_fd, double(:read_int => max_fd))
|
||||
expect(multi).to receive(:sleep).with(0.001)
|
||||
end
|
||||
|
||||
it "waits 100ms" do
|
||||
multi.method(:set_fds).call(timeout)
|
||||
end
|
||||
end
|
||||
|
||||
context "when max_fd not -1" do
|
||||
context "when code smaller zero" do
|
||||
before { expect(Ethon::Curl).to receive(:select).and_return(-1) }
|
||||
|
||||
it "raises Select error" do
|
||||
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::Select)
|
||||
end
|
||||
end
|
||||
|
||||
context "when code bigger or equal zero" do
|
||||
before { expect(Ethon::Curl).to receive(:select).and_return(0) }
|
||||
|
||||
it "doesn't raise" do
|
||||
expect{ multi.method(:set_fds).call(timeout) }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when code not ok" do
|
||||
before { expect(Ethon::Curl).to receive(:multi_fdset).and_return(:not_ok) }
|
||||
|
||||
it "raises MultiFdset error" do
|
||||
expect{ multi.method(:set_fds).call(timeout) }.to raise_error(Ethon::Errors::MultiFdset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#perform" do
|
||||
context "when no easy handles" do
|
||||
it "returns nil" do
|
||||
expect(multi.perform).to be_nil
|
||||
end
|
||||
|
||||
it "logs" do
|
||||
expect(Ethon.logger).to receive(:debug).twice
|
||||
multi.perform
|
||||
end
|
||||
end
|
||||
|
||||
context "when easy handle" do
|
||||
before do
|
||||
easy.url = "http://localhost:3001/"
|
||||
multi.add(easy)
|
||||
end
|
||||
|
||||
it "requests" do
|
||||
multi.perform
|
||||
end
|
||||
|
||||
it "sets easy" do
|
||||
multi.perform
|
||||
expect(easy.response_code).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
context "when four easy handles" do
|
||||
let(:easies) do
|
||||
ary = []
|
||||
4.times do
|
||||
ary << another_easy = Ethon::Easy.new
|
||||
another_easy.url = "http://localhost:3001/"
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
before do
|
||||
easies.each { |e| multi.add(e) }
|
||||
multi.perform
|
||||
end
|
||||
|
||||
it "sets response codes" do
|
||||
expect(easies.all?{ |e| e.response_code == 200 }).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ongoing?" do
|
||||
context "when easy_handles" do
|
||||
before { multi.easy_handles << 1 }
|
||||
|
||||
context "when running_count not greater 0" do
|
||||
before { multi.instance_variable_set(:@running_count, 0) }
|
||||
|
||||
it "returns true" do
|
||||
expect(multi.method(:ongoing?).call).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context "when running_count greater 0" do
|
||||
before { multi.instance_variable_set(:@running_count, 1) }
|
||||
|
||||
it "returns true" do
|
||||
expect(multi.method(:ongoing?).call).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when no easy_handles" do
|
||||
context "when running_count not greater 0" do
|
||||
before { multi.instance_variable_set(:@running_count, 0) }
|
||||
|
||||
it "returns false" do
|
||||
expect(multi.method(:ongoing?).call).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context "when running_count greater 0" do
|
||||
before { multi.instance_variable_set(:@running_count, 1) }
|
||||
|
||||
it "returns true" do
|
||||
expect(multi.method(:ongoing?).call).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#init_vars" do
|
||||
it "sets @timeout" do
|
||||
expect(multi.instance_variable_get(:@timeout)).to be_a(FFI::MemoryPointer)
|
||||
end
|
||||
|
||||
it "sets @timeval" do
|
||||
expect(multi.instance_variable_get(:@timeval)).to be_a(Ethon::Curl::Timeval)
|
||||
end
|
||||
|
||||
it "sets @fd_read" do
|
||||
expect(multi.instance_variable_get(:@fd_read)).to be_a(Ethon::Curl::FDSet)
|
||||
end
|
||||
|
||||
it "sets @fd_write" do
|
||||
expect(multi.instance_variable_get(:@fd_write)).to be_a(Ethon::Curl::FDSet)
|
||||
end
|
||||
|
||||
it "sets @fd_excep" do
|
||||
expect(multi.instance_variable_get(:@fd_excep)).to be_a(Ethon::Curl::FDSet)
|
||||
end
|
||||
|
||||
it "sets @max_fd" do
|
||||
expect(multi.instance_variable_get(:@max_fd)).to be_a(FFI::MemoryPointer)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reset_fds" do
|
||||
after { multi.method(:reset_fds).call }
|
||||
|
||||
it "resets @fd_read" do
|
||||
expect(multi.instance_variable_get(:@fd_read)).to receive(:clear)
|
||||
end
|
||||
|
||||
it "resets @fd_write" do
|
||||
expect(multi.instance_variable_get(:@fd_write)).to receive(:clear)
|
||||
end
|
||||
|
||||
it "resets @fd_excep" do
|
||||
expect(multi.instance_variable_get(:@fd_excep)).to receive(:clear)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#check" do
|
||||
it { skip("untested") }
|
||||
end
|
||||
|
||||
describe "#run" do
|
||||
it { skip("untested") }
|
||||
end
|
||||
|
||||
describe "#trigger" do
|
||||
it "calls multi perform" do
|
||||
expect(Ethon::Curl).to receive(:multi_perform)
|
||||
multi.send(:trigger, pointer)
|
||||
end
|
||||
|
||||
it "sets running count" do
|
||||
multi.instance_variable_set(:@running_count, nil)
|
||||
multi.send(:trigger, pointer)
|
||||
expect(multi.instance_variable_get(:@running_count)).to_not be_nil
|
||||
end
|
||||
|
||||
it "returns multi perform code" do
|
||||
expect(Ethon::Curl).to receive(:multi_perform).and_return(:ok)
|
||||
expect(multi.send(:trigger, pointer)).to eq(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,182 @@
|
||||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ethon::Multi::Options do
|
||||
let(:multi) { Ethon::Multi.new }
|
||||
|
||||
[
|
||||
:maxconnects, :pipelining, :socketdata, :socketfunction,
|
||||
:timerdata, :timerfunction, :max_total_connections
|
||||
].each do |name|
|
||||
describe "#{name}=" do
|
||||
it "responds_to" do
|
||||
expect(multi).to respond_to("#{name}=")
|
||||
end
|
||||
|
||||
it "sets option" do
|
||||
expect(Ethon::Curl).to receive(:set_option).with(name, anything, anything, anything)
|
||||
multi.method("#{name}=").call(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "socket_action mode" do
|
||||
let(:multi) { Ethon::Multi.new(execution_mode: :socket_action) }
|
||||
|
||||
describe "#socketfunction callbacks" do
|
||||
it "allows multi_code return values" do
|
||||
calls = []
|
||||
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
||||
calls << what
|
||||
:ok
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
multi.add(easy)
|
||||
expect(calls).to eq([])
|
||||
5.times do
|
||||
multi.socket_action
|
||||
break unless calls.empty?
|
||||
sleep 0.1
|
||||
end
|
||||
expect(calls.last).to eq(:in).or(eq(:out))
|
||||
multi.delete(easy)
|
||||
expect(calls.last).to eq(:remove)
|
||||
end
|
||||
|
||||
it "allows integer return values (compatibility)" do
|
||||
called = false
|
||||
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
||||
called = true
|
||||
0
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
multi.add(easy)
|
||||
5.times do
|
||||
multi.socket_action
|
||||
break if called
|
||||
sleep 0.1
|
||||
end
|
||||
multi.delete(easy)
|
||||
|
||||
expect(called).to be_truthy
|
||||
end
|
||||
|
||||
it "errors on invalid return codes" do
|
||||
called = false
|
||||
multi.socketfunction = proc do |handle, sock, what, userp, socketp|
|
||||
called = true
|
||||
"hi"
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
multi.add(easy)
|
||||
expect {
|
||||
5.times do
|
||||
multi.socket_action
|
||||
break if called
|
||||
sleep 0.1
|
||||
end
|
||||
}.to raise_error(ArgumentError)
|
||||
expect { multi.delete(easy) }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#timerfunction callbacks" do
|
||||
it "allows multi_code return values" do
|
||||
calls = []
|
||||
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
||||
calls << timeout_ms
|
||||
:ok
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
multi.add(easy)
|
||||
expect(calls.last).to be >= 0 # adds an immediate timeout
|
||||
|
||||
multi.delete(easy)
|
||||
expect(calls.last).to eq(-1) # cancels the timer
|
||||
end
|
||||
|
||||
it "allows integer return values (compatibility)" do
|
||||
called = false
|
||||
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
||||
called = true
|
||||
0
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
multi.add(easy)
|
||||
multi.socket_action
|
||||
multi.delete(easy)
|
||||
|
||||
expect(called).to be_truthy
|
||||
end
|
||||
|
||||
it "errors on invalid return codes" do
|
||||
called = false
|
||||
multi.timerfunction = proc do |handle, timeout_ms, userp|
|
||||
called = true
|
||||
"hi"
|
||||
end
|
||||
|
||||
easy = Ethon::Easy.new
|
||||
easy.url = "http://localhost:3001/?delay=1"
|
||||
expect { multi.add(easy) }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#value_for" do
|
||||
context "when option in bool" do
|
||||
context "when value true" do
|
||||
let(:value) { true }
|
||||
|
||||
it "returns 1" do
|
||||
expect(multi.method(:value_for).call(value, :bool)).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when value false" do
|
||||
let(:value) { false }
|
||||
|
||||
it "returns 0" do
|
||||
expect(multi.method(:value_for).call(value, :bool)).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
context "when value in int" do
|
||||
let(:value) { "2" }
|
||||
|
||||
it "returns value casted to int" do
|
||||
expect(multi.method(:value_for).call(value, :int)).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context "when value in unspecific_options" do
|
||||
context "when value a string" do
|
||||
let(:value) { "www.example.\0com" }
|
||||
|
||||
it "returns zero byte escaped string" do
|
||||
expect(multi.method(:value_for).call(value, nil)).to eq("www.example.\\0com")
|
||||
end
|
||||
end
|
||||
|
||||
context "when value not a string" do
|
||||
let(:value) { 1 }
|
||||
|
||||
it "returns value" do
|
||||
expect(multi.method(:value_for).call(value, nil)).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ethon::Multi::Stack do
|
||||
let(:multi) { Ethon::Multi.new }
|
||||
let(:easy) { Ethon::Easy.new }
|
||||
|
||||
describe "#add" do
|
||||
context "when easy already added" do
|
||||
before { multi.add(easy) }
|
||||
|
||||
it "returns nil" do
|
||||
expect(multi.add(easy)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when easy new" do
|
||||
it "adds easy to multi" do
|
||||
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
||||
multi.add(easy)
|
||||
end
|
||||
|
||||
it "adds easy to easy_handles" do
|
||||
multi.add(easy)
|
||||
expect(multi.easy_handles).to include(easy)
|
||||
end
|
||||
end
|
||||
|
||||
context "when multi_add_handle fails" do
|
||||
it "raises multi add error" do
|
||||
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:bad_easy_handle)
|
||||
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
||||
end
|
||||
end
|
||||
|
||||
context "when multi cleaned up before" do
|
||||
it "raises multi add error" do
|
||||
Ethon::Curl.multi_cleanup(multi.handle)
|
||||
expect{ multi.add(easy) }.to raise_error(Ethon::Errors::MultiAdd)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete" do
|
||||
context "when easy in easy_handles" do
|
||||
before { multi.add(easy) }
|
||||
|
||||
it "deletes easy from multi" do
|
||||
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:ok)
|
||||
multi.delete(easy)
|
||||
end
|
||||
|
||||
it "deletes easy from easy_handles" do
|
||||
multi.delete(easy)
|
||||
expect(multi.easy_handles).to_not include(easy)
|
||||
end
|
||||
end
|
||||
|
||||
context "when easy is not in easy_handles" do
|
||||
it "does nothing" do
|
||||
expect(Ethon::Curl).to receive(:multi_add_handle).and_return(:ok)
|
||||
multi.add(easy)
|
||||
end
|
||||
|
||||
it "adds easy to easy_handles" do
|
||||
multi.add(easy)
|
||||
expect(multi.easy_handles).to include(easy)
|
||||
end
|
||||
end
|
||||
|
||||
context "when multi_remove_handle fails" do
|
||||
before { multi.add(easy) }
|
||||
|
||||
it "raises multi remove error" do
|
||||
expect(Ethon::Curl).to receive(:multi_remove_handle).and_return(:bad_easy_handle)
|
||||
expect{ multi.delete(easy) }.to raise_error(Ethon::Errors::MultiRemove)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user