fiber_scheduler_spec 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75aebdfe9a1849f812c214b78eaa70a1d5ea759317e75ff1a895ae54eb9c6826
4
- data.tar.gz: 5a1f24342b3191c79b0e09b346d46b046009475b795b3842c16bd613e96535ff
3
+ metadata.gz: c1e20ced7c55e6f9566d1a0e802f217e0981be4ab4d2ccbd5c27ac9604e43f7b
4
+ data.tar.gz: 5b2234c2a5dee08864e7cbbd27105c9e1b70e610c379afbd74b6c86ab38e2d88
5
5
  SHA512:
6
- metadata.gz: 37f63683f1abc6822db5748059badef9a6799b18bac7a2e0eb1a35a12c295b1eca23f623eea81edca6246c731bcae6b12384ed01abd6a30ff526298edc06c863
7
- data.tar.gz: 36b1726dcf43159c40c5fd311382442653a772b132956e6bdc9f70e797e0b28b60fa13787b3204b399cc6e701c573af3e4c22de1d190316c02e887bb8126a390
6
+ metadata.gz: a7baa25d93db508fb5dde06e91979c30df901b817e5dc878369873c9932af07a0c2be2e4587be3160c1c43e0672e0872470032dc600135585cf6fe9b97200405
7
+ data.tar.gz: 20471ba20badf88e691affb068309e7734a67d1f73e9d8d272d6de460040795b1b8e305fb09221908cdcdf9a281d6d066309d56d7f733c4cd126499669c33928
@@ -0,0 +1,47 @@
1
+ require "rspec"
2
+ require "socket"
3
+ require_relative "context"
4
+
5
+ module FiberSchedulerSpec
6
+ module AddressResolve
7
+ end
8
+ end
9
+
10
+ RSpec.shared_examples FiberSchedulerSpec::AddressResolve do
11
+ include_context FiberSchedulerSpec::Context
12
+
13
+ context "Addrinfo.getaddrinfo" do
14
+ let(:order) { [] }
15
+
16
+ def operations
17
+ Fiber.schedule do
18
+ order << 1
19
+ Addrinfo.getaddrinfo("example.com", 80, :AF_INET, :STREAM)
20
+ order << 5
21
+ end
22
+
23
+ order << 2
24
+
25
+ Fiber.schedule do
26
+ order << 3
27
+ Addrinfo.getaddrinfo("example.com", 80, :AF_INET, :STREAM)
28
+ order << 6
29
+ end
30
+ order << 4
31
+ end
32
+
33
+ it "calls #address_resolve" do
34
+ expect_any_instance_of(scheduler_class)
35
+ .to receive(:address_resolve).exactly(2).times
36
+ .and_call_original
37
+
38
+ setup
39
+ end
40
+
41
+ it "behaves async" do
42
+ setup
43
+
44
+ expect(order).to eq (1..6).to_a
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,53 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module BlockUnblock
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::BlockUnblock do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "Addrinfo.getaddrinfo" do
12
+ let(:order) { [] }
13
+ let(:queue) { Thread::Queue.new }
14
+ let(:item) { "item" }
15
+ let(:popped_items) { [] }
16
+
17
+ def operations
18
+ Fiber.schedule do
19
+ order << 1
20
+ popped_items << queue.pop
21
+ order << 6
22
+ end
23
+
24
+ order << 2
25
+
26
+ Fiber.schedule do
27
+ order << 3
28
+ queue.push(item)
29
+ order << 4
30
+ end
31
+
32
+ order << 5
33
+ end
34
+
35
+ it "calls #block and #unblock" do
36
+ expect_any_instance_of(scheduler_class)
37
+ .to receive(:block).once
38
+ .and_call_original
39
+ expect_any_instance_of(scheduler_class)
40
+ .to receive(:unblock).once
41
+ .and_call_original
42
+
43
+ setup
44
+ end
45
+
46
+ it "behaves async" do
47
+ setup
48
+
49
+ expect(popped_items).to contain_exactly(item)
50
+ expect(order).to eq (1..6).to_a
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,83 @@
1
+ require "rspec"
2
+ require_relative "context"
3
+
4
+ module FiberSchedulerSpec
5
+ module Close
6
+ end
7
+ end
8
+
9
+ RSpec.shared_examples FiberSchedulerSpec::Close do
10
+ include_context FiberSchedulerSpec::Context
11
+
12
+ # TODO: should closing a scheduler also set Fiber.scheduler to nil?
13
+ context "without #run" do
14
+ let(:order) { [] }
15
+
16
+ def operations
17
+ Fiber.schedule do
18
+ order << 2
19
+ end
20
+ order << 1
21
+ end
22
+
23
+ # NOTE: this example does not use 'setup', #run should not be invoked
24
+ if method_defined?(:default_setup)
25
+ # skipping if user overrode setup
26
+ it "calls #close" do
27
+ expect(scheduler)
28
+ .to receive(:close).once
29
+ .and_call_original
30
+
31
+ Thread.new do
32
+ Fiber.set_scheduler(scheduler)
33
+
34
+ operations
35
+ end.join
36
+ end
37
+ end
38
+
39
+ it "behaves async" do
40
+ setup
41
+
42
+ expect(order).to contain_exactly(1, 2)
43
+ end
44
+ end
45
+
46
+ context "with #run" do
47
+ let(:order) { [] }
48
+
49
+ def operations
50
+ Fiber.schedule do
51
+ order << 2
52
+ end
53
+
54
+ order << 1
55
+ Fiber.scheduler.run
56
+ order << 3
57
+
58
+ Fiber.schedule do
59
+ order << 4
60
+ end
61
+
62
+ order << 5
63
+ end
64
+
65
+ it "calls #close" do
66
+ expect(scheduler)
67
+ .to receive(:close).once
68
+ .and_call_original
69
+
70
+ Thread.new do
71
+ Fiber.set_scheduler(scheduler)
72
+ operations
73
+ scheduler.run
74
+ end.join
75
+ end
76
+
77
+ it "behaves async" do
78
+ setup
79
+
80
+ expect(order).to contain_exactly(1, 2, 3, 4, 5)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,30 @@
1
+ require "rspec"
2
+
3
+ module FiberSchedulerSpec
4
+ module Context
5
+ end
6
+ end
7
+
8
+ RSpec.shared_context FiberSchedulerSpec::Context do
9
+ unless method_defined?(:scheduler_class)
10
+ let(:scheduler_class) { described_class }
11
+ end
12
+ unless method_defined?(:scheduler)
13
+ subject(:scheduler) { scheduler_class.new }
14
+ end
15
+ def setup
16
+ ::Fiber.set_scheduler(scheduler)
17
+
18
+ operations
19
+
20
+ scheduler.run
21
+ end
22
+
23
+ around do |example|
24
+ result = Thread.new do
25
+ example.run
26
+ end.join(1)
27
+
28
+ expect(result).to be_a Thread # failure means spec timed out
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module Fiber
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::Fiber do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "Fiber.schedule" do
12
+ let(:fibers) { [] }
13
+ let(:fiber) { fibers.first }
14
+
15
+ def operations
16
+ fibers << Fiber.schedule {}
17
+ end
18
+
19
+ it "calls #fiber" do
20
+ expect_any_instance_of(scheduler_class)
21
+ .to receive(:fiber).once
22
+ .and_call_original
23
+
24
+ setup
25
+ end
26
+
27
+ it "creates a fiber" do
28
+ # Prevent GC running inbetween two ObjectSpace calls.
29
+ GC.disable
30
+
31
+ before = ObjectSpace.each_object(Fiber).count
32
+ setup
33
+ after = ObjectSpace.each_object(Fiber).count
34
+
35
+ # The after - before is > 1 with the built-in selector.
36
+ expect(after - before).to be >= 1
37
+ ensure
38
+ GC.enable
39
+ end
40
+
41
+ it "creates a non-blocking fiber" do
42
+ setup
43
+
44
+ expect(fiber).to be_a Fiber
45
+ expect(fiber).not_to be_blocking
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,82 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module IOWait
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::IOWait do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "UNIXSocket#wait_readable" do
12
+ context "without a timeout" do
13
+ let(:order) { [] }
14
+ let(:pair) { UNIXSocket.pair }
15
+ let(:reader) { pair.first }
16
+ let(:writer) { pair.last }
17
+
18
+ def operations
19
+ Fiber.schedule do
20
+ order << 1
21
+ reader.wait_readable
22
+ reader.close
23
+ order << 6
24
+ end
25
+
26
+ order << 2
27
+
28
+ Fiber.schedule do
29
+ order << 3
30
+ writer.write(".")
31
+ writer.close
32
+ order << 4
33
+ end
34
+ order << 5
35
+ end
36
+
37
+ it "behaves async" do
38
+ setup
39
+
40
+ expect(order).to eq (1..6).to_a
41
+ end
42
+
43
+ it "calls #io_wait" do
44
+ expect_any_instance_of(scheduler_class)
45
+ .to receive(:io_wait).once
46
+ .and_call_original
47
+
48
+ setup
49
+ end
50
+ end
51
+
52
+ context "with a timeout" do
53
+ let(:order) { [] }
54
+ let(:pair) { UNIXSocket.pair }
55
+ let(:reader) { pair.first }
56
+ let(:writer) { pair.last }
57
+
58
+ def operations
59
+ Fiber.schedule do
60
+ order << 1
61
+ reader.wait_readable(0.001)
62
+ order << 3
63
+ end
64
+ order << 2
65
+ end
66
+
67
+ it "behaves async" do
68
+ setup
69
+
70
+ expect(order).to eq (1..3).to_a
71
+ end
72
+
73
+ it "calls #io_wait" do
74
+ expect_any_instance_of(scheduler_class)
75
+ .to receive(:io_wait).once
76
+ .and_call_original
77
+
78
+ setup
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,97 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module KernelSleep
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::KernelSleep do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "Kernel.sleep" do
12
+ let(:order) { [] }
13
+ let(:times) { [] }
14
+ let(:duration) { times[1] - times[0] }
15
+
16
+ context "multiple sleep operations" do
17
+ let(:interval) { 0.1 }
18
+
19
+ def operations
20
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
21
+
22
+ Fiber.schedule do
23
+ order << 1
24
+ sleep interval
25
+ order << 5
26
+ end
27
+
28
+ order << 2
29
+
30
+ Fiber.schedule do
31
+ order << 3
32
+ sleep interval
33
+ order << 6
34
+ end
35
+
36
+ order << 4
37
+ end
38
+
39
+ it "calls #kernel_sleep" do
40
+ expect_any_instance_of(scheduler_class)
41
+ .to receive(:kernel_sleep).exactly(2).times
42
+ .and_call_original
43
+
44
+ setup
45
+ end
46
+
47
+ it "behaves async" do
48
+ setup
49
+
50
+ expect(order).to eq (1..6).to_a
51
+ end
52
+
53
+ it "runs operations in parallel" do
54
+ setup
55
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
56
+
57
+ expect(duration).to be >= interval
58
+ expect(duration).to be < (interval * 1.2)
59
+ end
60
+ end
61
+
62
+ context "sleep 0" do
63
+ def operations
64
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
65
+
66
+ Fiber.schedule do
67
+ order << 1
68
+ sleep 0
69
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
70
+ order << 3
71
+ end
72
+ order << 2
73
+ end
74
+
75
+ it "calls #kernel_sleep" do
76
+ expect_any_instance_of(scheduler_class)
77
+ .to receive(:kernel_sleep).once
78
+ .and_call_original
79
+
80
+ setup
81
+ end
82
+
83
+ it "behaves async" do
84
+ setup
85
+
86
+ expect(order).to eq (1..3).to_a
87
+ end
88
+
89
+ it "runs the operation in no time" do
90
+ setup
91
+
92
+ # No sleeping was performed at all.
93
+ expect(duration).to be < 0.0005
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,61 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module ProcessWait
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::ProcessWait do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "Process.wait" do
12
+ let(:interval_short) { 0.09 }
13
+ let(:interval) { 0.1 }
14
+ let(:order) { [] }
15
+ let(:times) { [] }
16
+ let(:duration) { times[1] - times[0] }
17
+ def operations
18
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
19
+
20
+ Fiber.schedule do
21
+ order << 1
22
+ # This interval is shorter so we're certain it will finish before the
23
+ # other fiber.
24
+ Process.wait(spawn("sleep #{interval_short}"))
25
+ order << 5
26
+ end
27
+
28
+ order << 2
29
+
30
+ Fiber.schedule do
31
+ order << 3
32
+ Process.wait(spawn("sleep #{interval}"))
33
+ order << 6
34
+ end
35
+
36
+ order << 4
37
+ end
38
+
39
+ it "calls #process_wait" do
40
+ expect_any_instance_of(scheduler_class)
41
+ .to receive(:process_wait).exactly(2).times
42
+ .and_call_original
43
+
44
+ setup
45
+ end
46
+
47
+ it "behaves async" do
48
+ setup
49
+
50
+ expect(order).to eq (1..6).to_a
51
+ end
52
+
53
+ it "runs operations in parallel" do
54
+ setup
55
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
56
+
57
+ expect(duration).to be >= interval
58
+ expect(duration).to be < (interval * 1.5)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,62 @@
1
+ require_relative "context"
2
+
3
+ module FiberSchedulerSpec
4
+ module SocketIO
5
+ end
6
+ end
7
+
8
+ RSpec.shared_examples FiberSchedulerSpec::SocketIO do
9
+ include_context FiberSchedulerSpec::Context
10
+
11
+ context "UNIXSocket.pair" do
12
+ let(:order) { [] }
13
+ let(:pair) { UNIXSocket.pair }
14
+ let(:reader) { pair.first }
15
+ let(:writer) { pair.last }
16
+ let(:messages) { [] }
17
+ let(:sent) { "ruby" }
18
+ let(:received) { messages.first }
19
+
20
+ def operations
21
+ Fiber.schedule do
22
+ order << 1
23
+ messages << reader.read(sent.size)
24
+ reader.close
25
+ order << 6
26
+ end
27
+
28
+ order << 2
29
+
30
+ Fiber.schedule do
31
+ order << 3
32
+ writer.write(sent)
33
+ writer.close
34
+ order << 4
35
+ end
36
+ order << 5
37
+ end
38
+
39
+ it "calls #io_read and #io_write" do
40
+ expect_any_instance_of(scheduler_class)
41
+ .to receive(:io_read).once
42
+ .and_call_original
43
+ expect_any_instance_of(scheduler_class)
44
+ .to receive(:io_write).once
45
+ .and_call_original
46
+
47
+ setup
48
+ end
49
+
50
+ it "writes and reads a message" do
51
+ setup
52
+
53
+ expect(received).to eq sent
54
+ end
55
+
56
+ it "behaves async" do
57
+ setup
58
+
59
+ expect(order).to eq (1..6).to_a
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,87 @@
1
+ require "timeout"
2
+ require_relative "context"
3
+
4
+ module FiberSchedulerSpec
5
+ module TimeoutAfter
6
+ end
7
+ end
8
+
9
+ RSpec.shared_examples FiberSchedulerSpec::TimeoutAfter do
10
+ include_context FiberSchedulerSpec::Context
11
+
12
+ context "Timeout.timeout" do
13
+ let(:order) { [] }
14
+ let(:times) { [] }
15
+ let(:duration) { times[1] - times[0] }
16
+ let(:sleep_duration) { 0.01 }
17
+
18
+ def operations
19
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
20
+ Fiber.schedule do
21
+ begin
22
+ order << 1
23
+ Timeout.timeout(timeout) do
24
+ order << 2
25
+ sleep sleep_duration
26
+ order << 4
27
+ end
28
+ order << 5
29
+ rescue Timeout::Error
30
+ order << 6
31
+ end
32
+ times << Process.clock_gettime(Process::CLOCK_MONOTONIC)
33
+ end
34
+ order << 3
35
+ end
36
+
37
+ context "when the operation times out" do
38
+ let(:timeout) { 0.001 }
39
+
40
+ it "calls #timeout_after" do
41
+ expect_any_instance_of(scheduler_class)
42
+ .to receive(:timeout_after)
43
+ .and_call_original
44
+
45
+ setup
46
+ end
47
+
48
+ it "behaves async" do
49
+ setup
50
+
51
+ expect(order).to eq [1, 2, 3, 6]
52
+ end
53
+
54
+ it "times out early" do
55
+ setup
56
+
57
+ expect(duration).to be >= timeout
58
+ expect(duration).to be < (timeout * 10)
59
+ end
60
+ end
61
+
62
+ context "when the operation doesn't time out" do
63
+ let(:timeout) { 0.1 }
64
+
65
+ it "calls #timeout_after" do
66
+ expect_any_instance_of(scheduler_class)
67
+ .to receive(:timeout_after)
68
+ .and_call_original
69
+
70
+ setup
71
+ end
72
+
73
+ it "behaves async" do
74
+ setup
75
+
76
+ expect(order).to eq (1..5).to_a
77
+ end
78
+
79
+ it "finishes the operation" do
80
+ setup
81
+
82
+ expect(duration).to be >= sleep_duration
83
+ expect(duration).to be < (sleep_duration * 1.5)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,3 +1,3 @@
1
1
  module FiberSchedulerSpec
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,2 +1,21 @@
1
- module FiberSchedulerSpec
1
+ require_relative "fiber_scheduler_spec/address_resolve"
2
+ require_relative "fiber_scheduler_spec/block_unblock"
3
+ require_relative "fiber_scheduler_spec/close"
4
+ require_relative "fiber_scheduler_spec/fiber"
5
+ require_relative "fiber_scheduler_spec/io_wait"
6
+ require_relative "fiber_scheduler_spec/kernel_sleep"
7
+ require_relative "fiber_scheduler_spec/process_wait"
8
+ require_relative "fiber_scheduler_spec/socket_io"
9
+ require_relative "fiber_scheduler_spec/timeout_after"
10
+
11
+ RSpec.shared_examples FiberSchedulerSpec do
12
+ include_examples FiberSchedulerSpec::AddressResolve
13
+ include_examples FiberSchedulerSpec::BlockUnblock
14
+ include_examples FiberSchedulerSpec::Close
15
+ include_examples FiberSchedulerSpec::Fiber
16
+ include_examples FiberSchedulerSpec::IOWait
17
+ include_examples FiberSchedulerSpec::KernelSleep
18
+ include_examples FiberSchedulerSpec::ProcessWait
19
+ include_examples FiberSchedulerSpec::SocketIO
20
+ include_examples FiberSchedulerSpec::TimeoutAfter
2
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiber_scheduler_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno Sutic
@@ -17,7 +17,7 @@ dependencies:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.11'
20
- type: :development
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
@@ -45,6 +45,16 @@ extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
47
  - lib/fiber_scheduler_spec.rb
48
+ - lib/fiber_scheduler_spec/address_resolve.rb
49
+ - lib/fiber_scheduler_spec/block_unblock.rb
50
+ - lib/fiber_scheduler_spec/close.rb
51
+ - lib/fiber_scheduler_spec/context.rb
52
+ - lib/fiber_scheduler_spec/fiber.rb
53
+ - lib/fiber_scheduler_spec/io_wait.rb
54
+ - lib/fiber_scheduler_spec/kernel_sleep.rb
55
+ - lib/fiber_scheduler_spec/process_wait.rb
56
+ - lib/fiber_scheduler_spec/socket_io.rb
57
+ - lib/fiber_scheduler_spec/timeout_after.rb
48
58
  - lib/fiber_scheduler_spec/version.rb
49
59
  homepage: https://github.com/bruno-/fiber_scheduler_spec
50
60
  licenses: