random-port 0.7.4 → 0.7.5
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 +4 -4
- data/.0pdd.yml +1 -1
- data/.github/workflows/actionlint.yml +1 -1
- data/.github/workflows/codecov.yml +5 -2
- data/.github/workflows/copyrights.yml +1 -1
- data/.github/workflows/markdown-lint.yml +1 -1
- data/.github/workflows/pdd.yml +2 -2
- data/.github/workflows/rake.yml +4 -4
- data/.github/workflows/xcop.yml +1 -1
- data/.gitignore +1 -1
- data/.rubocop.yml +7 -1
- data/.rultor.yml +1 -1
- data/Gemfile +5 -3
- data/LICENSE.txt +1 -1
- data/Rakefile +1 -1
- data/lib/random-port/module.rb +2 -2
- data/lib/random-port/pool.rb +20 -16
- data/lib/random-port.rb +1 -1
- data/random-port.gemspec +2 -2
- data/test/test__helper.rb +2 -1
- data/test/test_pool.rb +59 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 685cbbb964686d55837c88f3856b60207d3abaadb76e96ba9f9093bc8aba0a9c
|
4
|
+
data.tar.gz: 83e7ea6c0ac635b3c06cb1d190d3a991be61d4d534596ae10852c1a7a8df542d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48adf68a56c6ac4550287538b7cefa09cf033575e56dd8cdd6d53f0bc49004c87270f4c3b1d5c30576e721bf9c0589245c50854b494a3ec191588fc9e974253a
|
7
|
+
data.tar.gz: bd33bb2321ea165a1e9e546fa257db2a824572a07462af36ebd0e4e2fe93a4679c16f269170f51042046e139c7ed35a76bd4bd39338dd6a00777ddb4c1fb1214
|
data/.0pdd.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2018-
|
1
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -19,12 +19,13 @@
|
|
19
19
|
# SOFTWARE.
|
20
20
|
---
|
21
21
|
name: codecov
|
22
|
-
on:
|
22
|
+
'on':
|
23
23
|
push:
|
24
24
|
branches:
|
25
25
|
- master
|
26
26
|
jobs:
|
27
27
|
codecov:
|
28
|
+
timeout-minutes: 5
|
28
29
|
runs-on: ubuntu-24.04
|
29
30
|
steps:
|
30
31
|
- uses: actions/checkout@v4
|
@@ -36,3 +37,5 @@ jobs:
|
|
36
37
|
- uses: codecov/codecov-action@v5
|
37
38
|
with:
|
38
39
|
token: ${{ secrets.CODECOV_TOKEN }}
|
40
|
+
files: coverage/.resultset.json
|
41
|
+
fail_ci_if_error: true
|
data/.github/workflows/pdd.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2018-
|
1
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -19,7 +19,7 @@
|
|
19
19
|
# SOFTWARE.
|
20
20
|
---
|
21
21
|
name: pdd
|
22
|
-
on:
|
22
|
+
'on':
|
23
23
|
push:
|
24
24
|
branches:
|
25
25
|
- master
|
data/.github/workflows/rake.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2018-
|
1
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -19,7 +19,7 @@
|
|
19
19
|
# SOFTWARE.
|
20
20
|
---
|
21
21
|
name: rake
|
22
|
-
on:
|
22
|
+
'on':
|
23
23
|
push:
|
24
24
|
branches:
|
25
25
|
- master
|
@@ -27,8 +27,8 @@ on:
|
|
27
27
|
branches:
|
28
28
|
- master
|
29
29
|
jobs:
|
30
|
-
|
31
|
-
|
30
|
+
rake:
|
31
|
+
timeout-minutes: 5
|
32
32
|
strategy:
|
33
33
|
matrix:
|
34
34
|
os: [ubuntu-24.04, macos-15, windows-2022]
|
data/.github/workflows/xcop.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2018-
|
1
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -23,7 +23,11 @@ AllCops:
|
|
23
23
|
TargetRubyVersion: 2.3.3
|
24
24
|
NewCops: enable
|
25
25
|
SuggestExtensions: false
|
26
|
+
require:
|
27
|
+
- rubocop-minitest
|
26
28
|
|
29
|
+
Minitest/EmptyLineBeforeAssertionMethods:
|
30
|
+
Enabled: false
|
27
31
|
Layout/EmptyLineAfterGuardClause:
|
28
32
|
Enabled: false
|
29
33
|
Style/ClassAndModuleChildren:
|
@@ -44,3 +48,5 @@ Metrics/CyclomaticComplexity:
|
|
44
48
|
Max: 20
|
45
49
|
Metrics/AbcSize:
|
46
50
|
Max: 60
|
51
|
+
Metrics/ClassLength:
|
52
|
+
Max: 150
|
data/.rultor.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
4
4
|
#
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -24,9 +24,11 @@ source 'https://rubygems.org'
|
|
24
24
|
gemspec
|
25
25
|
|
26
26
|
gem 'minitest', '5.25.4', require: false
|
27
|
+
gem 'qbash', '>=0.2.2', require: false
|
27
28
|
gem 'rake', '13.2.1', require: false
|
28
|
-
gem 'rubocop', '1.
|
29
|
-
gem 'rubocop-
|
29
|
+
gem 'rubocop', '1.71.0', require: false
|
30
|
+
gem 'rubocop-minitest', '0.36.0', require: false
|
31
|
+
gem 'rubocop-rspec', '3.4.0', require: false
|
30
32
|
gem 'simplecov', '0.22.0', require: false
|
31
33
|
gem 'threads', '0.4.1', require: false
|
32
34
|
gem 'yard', '0.9.37', require: false
|
data/LICENSE.txt
CHANGED
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
4
4
|
#
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
# of this software and associated documentation files (the 'Software'), to deal
|
data/lib/random-port/module.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# (The MIT License)
|
4
4
|
#
|
5
|
-
# Copyright (c) 2018-
|
5
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -25,7 +25,7 @@
|
|
25
25
|
# The module for all classes.
|
26
26
|
#
|
27
27
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
28
|
-
# Copyright:: Copyright (c) 2018-
|
28
|
+
# Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko
|
29
29
|
# License:: MIT
|
30
30
|
module RandomPort
|
31
31
|
end
|
data/lib/random-port/pool.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# (The MIT License)
|
4
4
|
#
|
5
|
-
# Copyright (c) 2018-
|
5
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -44,7 +44,7 @@ require_relative 'module'
|
|
44
44
|
# passing <tt>FALSE</tt>.
|
45
45
|
#
|
46
46
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
47
|
-
# Copyright:: Copyright (c) 2018-
|
47
|
+
# Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko
|
48
48
|
# License:: MIT
|
49
49
|
class RandomPort::Pool
|
50
50
|
# If can't acquire by time out.
|
@@ -55,12 +55,13 @@ class RandomPort::Pool
|
|
55
55
|
# Ctor.
|
56
56
|
# @param [Boolean] sync Set it to FALSE if you want this pool to be NOT thread-safe
|
57
57
|
# @param [Integer] limit Set the maximum number of ports in the pool
|
58
|
-
|
58
|
+
# @param [Integer] start The next port to try
|
59
|
+
def initialize(sync: true, limit: 65_536, start: 1025)
|
59
60
|
@ports = []
|
60
61
|
@sync = sync
|
61
62
|
@monitor = Monitor.new
|
62
63
|
@limit = limit
|
63
|
-
@next =
|
64
|
+
@next = start
|
64
65
|
end
|
65
66
|
|
66
67
|
# Application wide pool of ports
|
@@ -133,7 +134,7 @@ class RandomPort::Pool
|
|
133
134
|
# @param [Integer] total How many ports to take
|
134
135
|
# @return [Array<Integer>|nil] Ports found or NIL if impossible now
|
135
136
|
def group(total)
|
136
|
-
return if @ports.count + total > @limit
|
137
|
+
return nil if @ports.count + total > @limit
|
137
138
|
opts = Array.new(0, total)
|
138
139
|
begin
|
139
140
|
(0..(total - 1)).each do |i|
|
@@ -141,23 +142,26 @@ class RandomPort::Pool
|
|
141
142
|
opts[i] = take(port)
|
142
143
|
end
|
143
144
|
rescue Errno::EADDRINUSE, SocketError
|
144
|
-
return
|
145
|
+
return nil
|
145
146
|
end
|
146
|
-
return if opts.any? { |p| @ports.include?(p) }
|
147
|
+
return nil if opts.any? { |p| @ports.include?(p) }
|
147
148
|
d = total * (total - 1) / 2
|
148
|
-
return unless opts.inject(&:+) - (total * opts.min) == d
|
149
|
+
return nil unless opts.inject(&:+) - (total * opts.min) == d
|
149
150
|
@ports += opts
|
150
151
|
opts
|
151
152
|
end
|
152
153
|
|
153
|
-
# Find one possible TCP port.
|
154
|
-
#
|
155
|
-
#
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
154
|
+
# Find one possible TCP port or raise exception if this port can't be used.
|
155
|
+
#
|
156
|
+
# If port is occupied, this method raises an error (+Errno::EADDRINUSE+).
|
157
|
+
#
|
158
|
+
# @param [Integer] port Suggested port number
|
159
|
+
# @return [Integer] The same port number
|
160
|
+
def take(port)
|
161
|
+
['127.0.0.1', '::1', '0.0.0.0', 'localhost'].each do |host|
|
162
|
+
TCPServer.new(host, port).close
|
163
|
+
end
|
164
|
+
port
|
161
165
|
end
|
162
166
|
|
163
167
|
def safe(&block)
|
data/lib/random-port.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# (The MIT License)
|
4
4
|
#
|
5
|
-
# Copyright (c) 2018-
|
5
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
# of this software and associated documentation files (the 'Software'), to deal
|
data/random-port.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# (The MIT License)
|
4
4
|
#
|
5
|
-
# Copyright (c) 2018-
|
5
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
28
28
|
s.required_ruby_version = '>=2.3'
|
29
29
|
s.name = 'random-port'
|
30
|
-
s.version = '0.7.
|
30
|
+
s.version = '0.7.5'
|
31
31
|
s.license = 'MIT'
|
32
32
|
s.summary = 'Random TCP port'
|
33
33
|
s.description = 'Reserves a random TCP port'
|
data/test/test__helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright (c) 2018-
|
3
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
4
4
|
#
|
5
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -24,4 +24,5 @@ $stdout.sync = true
|
|
24
24
|
|
25
25
|
require 'simplecov'
|
26
26
|
SimpleCov.start
|
27
|
+
|
27
28
|
require 'minitest/autorun'
|
data/test/test_pool.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# (The MIT License)
|
4
4
|
#
|
5
|
-
# Copyright (c) 2018-
|
5
|
+
# Copyright (c) 2018-2025 Yegor Bugayenko
|
6
6
|
#
|
7
7
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
# of this software and associated documentation files (the 'Software'), to deal
|
@@ -23,13 +23,15 @@
|
|
23
23
|
# SOFTWARE.
|
24
24
|
|
25
25
|
require 'minitest/autorun'
|
26
|
-
require '
|
26
|
+
require 'qbash'
|
27
|
+
require 'shellwords'
|
27
28
|
require 'socket'
|
29
|
+
require 'threads'
|
28
30
|
require_relative '../lib/random-port/pool'
|
29
31
|
|
30
32
|
# Pool test.
|
31
33
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
32
|
-
# Copyright:: Copyright (c) 2018-
|
34
|
+
# Copyright:: Copyright (c) 2018-2025 Yegor Bugayenko
|
33
35
|
# License:: MIT
|
34
36
|
class RandomPort::TestPool < Minitest::Test
|
35
37
|
def test_acquires_and_releases
|
@@ -37,8 +39,8 @@ class RandomPort::TestPool < Minitest::Test
|
|
37
39
|
port = pool.acquire
|
38
40
|
server = TCPServer.new('localhost', port)
|
39
41
|
server.close
|
40
|
-
|
41
|
-
|
42
|
+
refute_nil(port)
|
43
|
+
assert_predicate(port, :positive?)
|
42
44
|
pool.release(port)
|
43
45
|
end
|
44
46
|
|
@@ -56,11 +58,56 @@ class RandomPort::TestPool < Minitest::Test
|
|
56
58
|
assert_equal(0, pool.size)
|
57
59
|
end
|
58
60
|
|
61
|
+
def test_skips_truly_busy_port
|
62
|
+
port = RandomPort::Pool.new.acquire
|
63
|
+
server = TCPServer.new('127.0.0.1', port)
|
64
|
+
other = RandomPort::Pool.new(start: port).acquire
|
65
|
+
refute_equal(other, port)
|
66
|
+
server.close
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_skips_externally_busy_port
|
70
|
+
skip 'Not supported on Windows' if Gem.win_platform?
|
71
|
+
['127.0.0.1', 'localhost', '::1', '0.0.0.0'].each do |host|
|
72
|
+
Dir.mktmpdir do |home|
|
73
|
+
port = RandomPort::Pool.new.acquire
|
74
|
+
started = File.join(home, 'started.txt')
|
75
|
+
enough = File.join(home, 'enough.txt')
|
76
|
+
t =
|
77
|
+
Thread.new do
|
78
|
+
qbash(
|
79
|
+
[
|
80
|
+
'ruby', '-e',
|
81
|
+
Shellwords.escape(
|
82
|
+
"
|
83
|
+
require 'socket'
|
84
|
+
require 'fileutils'
|
85
|
+
TCPServer.new('#{host}', #{port})
|
86
|
+
FileUtils.touch('#{started}')
|
87
|
+
loop do
|
88
|
+
break if File.exist?('#{enough}')
|
89
|
+
end
|
90
|
+
"
|
91
|
+
)
|
92
|
+
]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
loop do
|
96
|
+
break if File.exist?(started)
|
97
|
+
end
|
98
|
+
other = RandomPort::Pool.new(start: port).acquire
|
99
|
+
FileUtils.touch(enough)
|
100
|
+
t.join
|
101
|
+
refute_equal(other, port)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
59
106
|
def test_acquires_and_releases_three_ports_in_block
|
60
107
|
pool = RandomPort::Pool.new(limit: 3)
|
61
108
|
assert_equal(0, pool.size)
|
62
109
|
pool.acquire(3, timeout: 16) do |ports|
|
63
|
-
|
110
|
+
assert_kind_of(Array, ports)
|
64
111
|
assert_equal(3, ports.count)
|
65
112
|
assert_equal(3, pool.size)
|
66
113
|
ports.each do |p|
|
@@ -73,8 +120,8 @@ class RandomPort::TestPool < Minitest::Test
|
|
73
120
|
|
74
121
|
def test_acquires_and_releases_in_block
|
75
122
|
result = RandomPort::Pool.new.acquire do |port|
|
76
|
-
|
77
|
-
|
123
|
+
refute_nil(port)
|
124
|
+
assert_predicate(port, :positive?)
|
78
125
|
123
|
79
126
|
end
|
80
127
|
assert_equal(123, result)
|
@@ -94,18 +141,18 @@ class RandomPort::TestPool < Minitest::Test
|
|
94
141
|
|
95
142
|
def test_acquires_and_releases_safely
|
96
143
|
pool = RandomPort::Pool.new
|
97
|
-
assert_raises do
|
144
|
+
assert_raises(StandardError) do
|
98
145
|
pool.acquire do
|
99
146
|
raise 'Itended'
|
100
147
|
end
|
101
148
|
end
|
102
|
-
|
149
|
+
assert_predicate(pool.count, :zero?)
|
103
150
|
end
|
104
151
|
|
105
152
|
def test_acquires_and_releases_from_singleton
|
106
153
|
RandomPort::Pool::SINGLETON.acquire do |port|
|
107
|
-
|
108
|
-
|
154
|
+
refute_nil(port)
|
155
|
+
assert_predicate(port, :positive?)
|
109
156
|
end
|
110
157
|
end
|
111
158
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: random-port
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tago
|