random-port 0.7.4 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|