test-cmd.rb 2.4.0 → 2.5.0

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: b091ee25f1e890969728e516747d3104c20076d2a71973e02ccc346a9eb1cc8e
4
- data.tar.gz: 4dbb449b245f30e89285b7cae84de42564e352a4f0790fcc3a8a048e21b3ede2
3
+ metadata.gz: a0790a7e8e68709204aeb42611543db79e3575615d6ed77ee0b166fff7827825
4
+ data.tar.gz: 7965cd9baf53560ff8648f0652876b3d31a969528b1419b5b975406ec37c13fc
5
5
  SHA512:
6
- metadata.gz: 43fb7bb3700e9f384c253c8ab36ec3a1ebb2ec5e807b3af1d57883a64bddb132838086fc15b242ce3a65b85092941d23a040b866b3e173bed4bf5254e2829690
7
- data.tar.gz: c314163ab04470596296e8644414bec98414955b7423a1cc5affe202184befedffaff5f1f63427b18dd192b356fe491d3ec85e1b1c5140857c185b3a7ea37402
6
+ metadata.gz: f0f7c63d381cbbd8c603c8fc7a1c22e7c73509b0a51965107c2fe2c23adf3c2b8cf5f243712b4f297f90aeb0535c01477ddd4518c10be6afd6a29b248c30af18
7
+ data.tar.gz: aee5d21c5948e17b29f0df43636bb18d396d6919efdc3a0fe6c03628ba85d5f3ae0e04cd03e61e51c33ad30aa88a2e7a840051cc704d3ac384957ea75d6f1cd1
data/README.md CHANGED
@@ -60,6 +60,15 @@ p Test::Command
60
60
  .new("tr", "a-z", "A-Z")
61
61
  .stdin("hello")
62
62
  .stdout # => "HELLO"
63
+
64
+ ##
65
+ # Limit how much data is read from stdout and
66
+ # stderr. At most 20k chars is read from both:
67
+ p Test::Command
68
+ .new("git")
69
+ .limit(stdout: 20_000, stderr: 20_000)
70
+ .argv("diff", "--cached")
71
+ .stdout
63
72
  ```
64
73
 
65
74
  <details>
@@ -4,5 +4,5 @@ end unless defined?(Test)
4
4
  class Test::Command
5
5
  ##
6
6
  # @string [String]
7
- VERSION = "2.4.0"
7
+ VERSION = "2.5.0"
8
8
  end
data/lib/test/command.rb CHANGED
@@ -34,6 +34,7 @@ class Test::Command
34
34
  @stdout = ""
35
35
  @stderr = ""
36
36
  @enoent = false
37
+ @limit = {stdout: nil, stderr: nil}
37
38
  end
38
39
 
39
40
  ##
@@ -67,6 +68,19 @@ class Test::Command
67
68
  tap { @stdin = data }
68
69
  end
69
70
 
71
+ ##
72
+ # Limits how many bytes of each stream are captured.
73
+ # A stream output beyond the limit is discarded, keeping
74
+ # memory bounded even for a command that streams a lot.
75
+ # @param [Integer, nil] stdout
76
+ # Max bytes of stdout to read, or nil for unlimited
77
+ # @param [Integer, nil] stderr
78
+ # Max bytes of stderr to read, or nil for unlimited
79
+ # @return [Test::Command]
80
+ def limit(stdout: nil, stderr: nil)
81
+ tap { @limit = {stdout:, stderr:} }
82
+ end
83
+
70
84
  ##
71
85
  # Spawns a command
72
86
  # @return [Test::Command]
@@ -85,8 +99,8 @@ class Test::Command
85
99
  @out.w.close
86
100
  @err.w.close
87
101
  @producer = Thread.new do
88
- @stdout = @out.r.read
89
- @stderr = @err.r.read
102
+ @stdout = read_limited(@out.r, @limit[:stdout])
103
+ @stderr = read_limited(@err.r, @limit[:stderr])
90
104
  Process.wait
91
105
  @status = $?
92
106
  ensure
@@ -232,6 +246,31 @@ class Test::Command
232
246
 
233
247
  private
234
248
 
249
+ ##
250
+ # Reads up to +max+ bytes from a stream, then drains
251
+ # the remainder so a child writing more than the limit
252
+ # does not block on a full pipe. When +max+ is nil the
253
+ # entire stream is read.
254
+ # @param [IO] io
255
+ # @param [Integer, nil] max
256
+ # @return [String]
257
+ def read_limited(io, max)
258
+ return io.read if max.nil?
259
+ buf = +""
260
+ remaining = max
261
+ while remaining > 0 && (chunk = io.read(remaining))
262
+ buf << chunk
263
+ remaining -= chunk.bytesize
264
+ end
265
+ ##
266
+ # Drain whatever is left so the child can exit:
267
+ # the drained bytes are discarded, keeping memory
268
+ # bounded.
269
+ while (chunk = io.read(16_384))
270
+ end
271
+ buf
272
+ end
273
+
235
274
  ##
236
275
  # Returns a pipe pair used for the command's
237
276
  # standard input. When no input is set, the child
data/test/cmd_test.rb CHANGED
@@ -58,6 +58,43 @@ class Test::Command
58
58
  end
59
59
  end
60
60
 
61
+ ##
62
+ # Test::Command#limit
63
+ class LimitTest < Test
64
+ def test_ruby_limit_stdout
65
+ assert_equal "happy",
66
+ ::Test::Command.new("printf", "%s", "happy days")
67
+ .limit(stdout: 5)
68
+ .stdout
69
+ end
70
+
71
+ def test_ruby_limit_stderr
72
+ assert_equal "smile",
73
+ ::Test::Command.new("sh", "-c", "echo smile >&2")
74
+ .limit(stderr: 5)
75
+ .stderr
76
+ end
77
+
78
+ def test_ruby_limit_nil_is_unlimited
79
+ expect = "a" * 20_000
80
+ assert_equal expect,
81
+ ::Test::Command.new("ruby", "-e", "print 'a' * 20_000")
82
+ .stdout
83
+ end
84
+
85
+ def test_ruby_limit_drains_remaining_output
86
+ cmd = ::Test::Command.new("sh", "-c", "printf 'happy days'")
87
+ .limit(stdout: 5)
88
+ .spawn
89
+ assert_equal true, cmd.spawned?
90
+ ##
91
+ # A full pipe would block forever: the
92
+ # drain lets the child finish and capture
93
+ # its status
94
+ assert_equal 0, cmd.exit_status
95
+ end
96
+ end
97
+
61
98
  ##
62
99
  # Test::Command#{exit_status, status, success?}
63
100
  class ExitStatusTest < Test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-cmd.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Gleeson