albino 1.2.3 → 1.3.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.
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'albino'
16
- s.version = '1.2.3'
17
- s.date = '2011-01-12'
16
+ s.version = '1.3.0'
17
+ s.date = '2011-03-03'
18
18
  s.rubyforge_project = 'albino'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
33
33
  ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
34
34
  s.require_paths = %w[lib]
35
35
 
36
+ s.add_dependency('posix-spawn')
36
37
  s.add_development_dependency('mocha')
37
38
 
38
39
  ## Leave this section as-is. It will be automatically generated from the
@@ -45,9 +46,10 @@ Gem::Specification.new do |s|
45
46
  Rakefile
46
47
  albino.gemspec
47
48
  lib/albino.rb
48
- lib/albino/process.rb
49
+ lib/albino/multi.rb
49
50
  test/albino_test.rb
50
- test/process_test.rb
51
+ test/multi_albino_test.rb
52
+ vendor/multipygmentize
51
53
  ]
52
54
  # = MANIFEST =
53
55
 
@@ -1,4 +1,5 @@
1
- require 'albino/process'
1
+ require 'posix-spawn'
2
+
2
3
  ##
3
4
  # Wrapper for the Pygments command line tool, pygmentize.
4
5
  #
@@ -44,8 +45,9 @@ require 'albino/process'
44
45
  #
45
46
  class Albino
46
47
  class ShellArgumentError < ArgumentError; end
48
+ include POSIX::Spawn
47
49
 
48
- VERSION = '1.2.3'
50
+ VERSION = '1.3.0'
49
51
 
50
52
  class << self
51
53
  attr_accessor :bin, :default_encoding, :timeout_threshold
@@ -69,7 +71,7 @@ class Albino
69
71
  proc_options[:timeout] = options.delete(:timeout) || self.class.timeout_threshold
70
72
  command = convert_options(options)
71
73
  command.unshift(bin)
72
- Process.new(command, env={}, proc_options.merge(:input => write_target))
74
+ Child.new(*(command + [proc_options.merge(:input => write_target)]))
73
75
  end
74
76
 
75
77
  def colorize(options = {})
@@ -0,0 +1,98 @@
1
+ require 'posix-spawn'
2
+
3
+ class Albino
4
+ if !const_defined?(:ShellArgumentError)
5
+ class ShellArgumentError < ArgumentError; end
6
+ end
7
+
8
+ # Wrapper for a custom multipygmentize script. Passes multiple code
9
+ # fragments in STDIN to Python. This assumes both Python and pygments are
10
+ # installed.
11
+ #
12
+ # Use like so:
13
+ #
14
+ # @syntaxer = Albino::Multi.new([ [:ruby, File.open("/some/file.rb")] ])
15
+ # puts @syntaxer.colorize
16
+ #
17
+ # It takes an Array of two-element arrays [lexer, code].
18
+ #
19
+ # You can also use Albino::Multi as a drop-in replacement. It currently has
20
+ # a few limitations however:
21
+ #
22
+ # * Only the HTML output format is supported.
23
+ # * UTF-8 encoding is forced.
24
+ #
25
+ # The default lexer is 'text'. You need to specify a lexer yourself;
26
+ # because we are using STDIN there is no auto-detect.
27
+ #
28
+ # To see all lexers and formatters available, run `pygmentize -L`.
29
+ class Multi
30
+ include POSIX::Spawn
31
+
32
+ class << self
33
+ attr_accessor :bin, :timeout_threshold
34
+ end
35
+
36
+ self.timeout_threshold = 10
37
+ self.bin = File.join(File.dirname(__FILE__), *%w(.. .. vendor multipygmentize))
38
+
39
+ # Initializes a new Albino::Multi and runs #colorize.
40
+ def self.colorize(*args)
41
+ new(*args).colorize
42
+ end
43
+
44
+ # This method accepts two forms of input:
45
+ #
46
+ # DEFAULT
47
+ #
48
+ # target - The Array of two-element [lexer, code] Arrays:
49
+ # lexer - The String lexer for the upcoming block of code.
50
+ # code - The String block of code to highlight.
51
+ #
52
+ # LEGACY
53
+ #
54
+ # target - The String block of code to highlight.
55
+ # lexer - The String lexer for the block of code.
56
+ #
57
+ # Albino#initialize also takes format and encoding which are ignored.
58
+ def initialize(target, lexer = :text, *args)
59
+ @spec = case target
60
+ when Array
61
+ @multi = true
62
+ target
63
+ else
64
+ [[lexer, target]]
65
+ end
66
+ end
67
+
68
+ # Colorizes the code blocks.
69
+ #
70
+ # options - Specify options for the child process:
71
+ # timeout - A Fixnum timeout for the child process.
72
+ #
73
+ # Returns an Array of HTML highlighted code block Strings if an array of
74
+ # targets are given to #initialize, or just a single HTML highlighted code
75
+ # block String.
76
+ def colorize(options = {})
77
+ options[:timeout] ||= self.class.timeout_threshold
78
+ options[:input] = @spec.inject([]) do |memo, (lexer, code)|
79
+ memo << lexer << SEPARATOR
80
+
81
+ if code.respond_to?(:read)
82
+ out = code.read
83
+ code.close
84
+ code = out
85
+ end
86
+
87
+ memo << code << SEPARATOR
88
+ end.join("")
89
+ child = Child.new(self.class.bin, options)
90
+ pieces = child.out.split(SEPARATOR)
91
+ @multi ? pieces : pieces.first
92
+ end
93
+
94
+ alias_method :to_s, :colorize
95
+
96
+ SEPARATOR = "\000".freeze
97
+ end
98
+ end
@@ -1,5 +1,5 @@
1
- require 'albino'
2
1
  require 'rubygems'
2
+ require 'albino'
3
3
  require 'test/unit'
4
4
  require 'tempfile'
5
5
  require 'mocha'
@@ -25,7 +25,8 @@ class AlbinoTest < Test::Unit::TestCase
25
25
 
26
26
  def test_works_with_strings
27
27
  syntaxer = Albino.new("class New\nend", :ruby)
28
- assert_match %r(highlight), syntaxer.colorize
28
+ assert_match %r(highlight), code=syntaxer.colorize
29
+ assert_match %(<span class="nc">New</span>\n), code
29
30
  end
30
31
 
31
32
  def test_works_with_files
@@ -55,4 +56,4 @@ class AlbinoTest < Test::Unit::TestCase
55
56
  @syntaxer.convert_options(:l => "'abc;'")
56
57
  end
57
58
  end
58
- end
59
+ end
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'albino/multi'
3
+ require 'test/unit'
4
+ require 'tempfile'
5
+ require 'mocha'
6
+
7
+ class MultiTest < Test::Unit::TestCase
8
+ def setup
9
+ @syntaxer = Albino::Multi.new(File.new(__FILE__), :ruby)
10
+ end
11
+
12
+ def test_defaults_to_text
13
+ syntaxer = Albino::Multi.new('abc')
14
+ regex = /span/
15
+ assert_no_match regex, syntaxer.colorize
16
+ end
17
+
18
+ def test_works_with_strings
19
+ syntaxer = Albino::Multi.new("class New\nend", :ruby)
20
+ assert_match %r(highlight), code=syntaxer.colorize
21
+ assert_match %(<span class="nc">New</span>\n), code
22
+ end
23
+
24
+ def test_works_with_multiple_code_fragments
25
+ syntaxer = Albino::Multi.new [
26
+ ['ruby', "class New\nend"],
27
+ ['python', "class New:\n pass"]]
28
+ codes = syntaxer.colorize
29
+ assert_equal 2, codes.size
30
+ assert_match %r(highlight), codes[0]
31
+ assert_match %r(highlight), codes[1]
32
+ assert_match %(<span class="nc">New</span>\n), codes[0]
33
+ assert_match %(<span class="p">:</span>), codes[1]
34
+ end
35
+
36
+ def test_works_with_files
37
+ contents = "class New\nend"
38
+ syntaxer = Albino::Multi.new(contents, :ruby)
39
+ file_output = syntaxer.colorize
40
+
41
+ Tempfile.open 'albino-test' do |tmp|
42
+ tmp << contents
43
+ tmp.flush
44
+ syntaxer = Albino::Multi.new(File.new(tmp.path), :ruby)
45
+ assert_equal file_output, syntaxer.colorize
46
+ end
47
+ end
48
+
49
+ def test_aliases_to_s
50
+ syntaxer = Albino::Multi.new(File.new(__FILE__), :ruby)
51
+ assert_equal @syntaxer.colorize, syntaxer.to_s
52
+ end
53
+
54
+ def test_class_method_colorize
55
+ assert_equal @syntaxer.colorize, Albino::Multi.colorize(File.new(__FILE__), :ruby)
56
+ end
57
+ end
58
+
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env python
2
+
3
+ # This script allows you to highlight multiple chunks of code with a single
4
+ # invocation. Expected input is on STDIN and takes the form of:
5
+ #
6
+ # <lexer>\000<code>\000<lexer>\000<code>...
7
+ #
8
+ # where <lexer> is the shortname of a Pygments lexer and <code> is the source
9
+ # code to be highlighted. Each lexer and code pair is separated with a NULL
10
+ # byte and pairs of lexer/code are also separated with NULL bytes.
11
+ #
12
+ # Output is a list of highlighted code blocks separated by NULL bytes in the
13
+ # same order in which they were received.
14
+
15
+ import sys, os, codecs
16
+
17
+ sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)
18
+
19
+ vpath = os.path.realpath(__file__).split("/")
20
+ vpath.pop()
21
+ vpath.pop()
22
+ vpath = "/".join(vpath)
23
+
24
+ from pygments import highlight
25
+ from pygments.lexers import get_lexer_by_name
26
+ from pygments.formatters import HtmlFormatter
27
+
28
+ parts = sys.stdin.read().split("\000")
29
+ newparts = []
30
+
31
+ for i in range(len(parts) / 2):
32
+ lang = parts[i * 2]
33
+ code = parts[i * 2 + 1]
34
+ try:
35
+ lexer = get_lexer_by_name(lang)
36
+ except:
37
+ lexer = get_lexer_by_name('text')
38
+ newparts.append([code, lexer])
39
+
40
+ def hl(spec):
41
+ code = spec[0]
42
+ lexer = spec[1]
43
+ try:
44
+ return highlight(code, lexer, HtmlFormatter())
45
+ except:
46
+ lexer = get_lexer_by_name('text')
47
+ return highlight(code, lexer, HtmlFormatter())
48
+
49
+ for spec in newparts:
50
+ sys.stdout.write(hl(spec))
51
+ sys.stdout.write("\000")
52
+
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: albino
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 27
4
5
  prerelease: false
5
6
  segments:
6
7
  - 1
7
- - 2
8
8
  - 3
9
- version: 1.2.3
9
+ - 0
10
+ version: 1.3.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Chris Wanstrath
@@ -14,21 +15,37 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2011-01-12 00:00:00 -08:00
18
+ date: 2011-03-03 00:00:00 -08:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
- name: mocha
22
+ name: posix-spawn
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
30
- type: :development
33
+ type: :runtime
31
34
  version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: mocha
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
32
49
  description: Ruby wrapper for pygmentize.
33
50
  email: chris@wanstrath.com
34
51
  executables: []
@@ -43,9 +60,10 @@ files:
43
60
  - Rakefile
44
61
  - albino.gemspec
45
62
  - lib/albino.rb
46
- - lib/albino/process.rb
63
+ - lib/albino/multi.rb
47
64
  - test/albino_test.rb
48
- - test/process_test.rb
65
+ - test/multi_albino_test.rb
66
+ - vendor/multipygmentize
49
67
  has_rdoc: true
50
68
  homepage: http://github.com/github/albino
51
69
  licenses: []
@@ -56,23 +74,27 @@ rdoc_options: []
56
74
  require_paths:
57
75
  - lib
58
76
  required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
59
78
  requirements:
60
79
  - - ">="
61
80
  - !ruby/object:Gem::Version
81
+ hash: 3
62
82
  segments:
63
83
  - 0
64
84
  version: "0"
65
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
66
87
  requirements:
67
88
  - - ">="
68
89
  - !ruby/object:Gem::Version
90
+ hash: 3
69
91
  segments:
70
92
  - 0
71
93
  version: "0"
72
94
  requirements: []
73
95
 
74
96
  rubyforge_project: albino
75
- rubygems_version: 1.3.6
97
+ rubygems_version: 1.3.7
76
98
  signing_key:
77
99
  specification_version: 2
78
100
  summary: Ruby wrapper for pygmentize.
@@ -1,294 +0,0 @@
1
- class Albino
2
- # Albino::Process includes logic for executing child processes and
3
- # reading/writing from their standard input, output, and error streams.
4
- #
5
- # Create an run a process to completion:
6
- #
7
- # >> process = Albino::Process.new(['git', '--help'])
8
- #
9
- # Retrieve stdout or stderr output:
10
- #
11
- # >> process.out
12
- # => "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]\n ..."
13
- # >> process.err
14
- # => ""
15
- #
16
- # Check process exit status information:
17
- #
18
- # >> process.status
19
- # => #<Process::Status: pid=80718,exited(0)>
20
- #
21
- # Albino::Process is designed to take all input in a single string and
22
- # provides all output as single strings. It is therefore not well suited
23
- # to streaming large quantities of data in and out of commands.
24
- #
25
- # Q: Why not use popen3 or hand-roll fork/exec code?
26
- #
27
- # - It's more efficient than popen3 and provides meaningful process
28
- # hierarchies because it performs a single fork/exec. (popen3 double forks
29
- # to avoid needing to collect the exit status and also calls
30
- # Process::detach which creates a Ruby Thread!!!!).
31
- #
32
- # - It's more portable than hand rolled pipe, fork, exec code because
33
- # fork(2) and exec(2) aren't available on all platforms. In those cases,
34
- # Albino::Process falls back to using whatever janky substitutes the platform
35
- # provides.
36
- #
37
- # - It handles all max pipe buffer hang cases, which is non trivial to
38
- # implement correctly and must be accounted for with either popen3 or
39
- # hand rolled fork/exec code.
40
- class Process
41
- # Create and execute a new process.
42
- #
43
- # argv - Array of [command, arg1, ...] strings to use as the new
44
- # process's argv. When argv is a String, the shell is used
45
- # to interpret the command.
46
- # env - The new process's environment variables. This is merged with
47
- # the current environment as if by ENV.merge(env).
48
- # options - Additional options:
49
- # :input => str to write str to the process's stdin.
50
- # :timeout => int number of seconds before we given up.
51
- # :max => total number of output bytes
52
- # A subset of Process:spawn options are also supported on all
53
- # platforms:
54
- # :chdir => str to start the process in different working dir.
55
- #
56
- # Returns a new Process instance that has already executed to completion.
57
- # The out, err, and status attributes are immediately available.
58
- def initialize(argv, env={}, options={})
59
- @argv = argv
60
- @env = env
61
-
62
- @options = options.dup
63
- @input = @options.delete(:input)
64
- @timeout = @options.delete(:timeout)
65
- @max = @options.delete(:max)
66
- @options.delete(:chdir) if @options[:chdir].nil?
67
-
68
- exec!
69
- end
70
-
71
- # All data written to the child process's stdout stream as a String.
72
- attr_reader :out
73
-
74
- # All data written to the child process's stderr stream as a String.
75
- attr_reader :err
76
-
77
- # A Process::Status object with information on how the child exited.
78
- attr_reader :status
79
-
80
- # Total command execution time (wall-clock time)
81
- attr_reader :runtime
82
-
83
- # Determine if the process did exit with a zero exit status.
84
- def success?
85
- @status && @status.success?
86
- end
87
-
88
- private
89
- # Execute command, write input, and read output. This is called
90
- # immediately when a new instance of this object is initialized.
91
- def exec!
92
- # when argv is a string, use /bin/sh to interpret command
93
- argv = @argv
94
- argv = ['/bin/sh', '-c', argv.to_str] if argv.respond_to?(:to_str)
95
-
96
- # spawn the process and hook up the pipes
97
- pid, stdin, stdout, stderr = popen4(@env, *(argv + [@options]))
98
-
99
- # async read from all streams into buffers
100
- @out, @err = read_and_write(@input, stdin, stdout, stderr, @timeout, @max)
101
-
102
- # grab exit status
103
- @status = waitpid(pid)
104
- rescue Object => boom
105
- [stdin, stdout, stderr].each { |fd| fd.close rescue nil }
106
- if @status.nil?
107
- ::Process.kill('TERM', pid) rescue nil
108
- @status = waitpid(pid) rescue nil
109
- end
110
- raise
111
- end
112
-
113
- # Exception raised when the total number of bytes output on the command's
114
- # stderr and stdout streams exceeds the maximum output size (:max option).
115
- class MaximumOutputExceeded < StandardError
116
- end
117
-
118
- # Exception raised when timeout is exceeded.
119
- class TimeoutExceeded < StandardError
120
- end
121
-
122
- # Maximum buffer size for reading
123
- BUFSIZE = (32 * 1024)
124
-
125
- # Start a select loop writing any input on the child's stdin and reading
126
- # any output from the child's stdout or stderr.
127
- #
128
- # input - String input to write on stdin. May be nil.
129
- # stdin - The write side IO object for the child's stdin stream.
130
- # stdout - The read side IO object for the child's stdout stream.
131
- # stderr - The read side IO object for the child's stderr stream.
132
- # timeout - An optional Numeric specifying the total number of seconds
133
- # the read/write operations should occur for.
134
- #
135
- # Returns an [out, err] tuple where both elements are strings with all
136
- # data written to the stdout and stderr streams, respectively.
137
- # Raises TimeoutExceeded when all data has not been read / written within
138
- # the duration specified in the timeout argument.
139
- # Raises MaximumOutputExceeded when the total number of bytes output
140
- # exceeds the amount specified by the max argument.
141
- def read_and_write(input, stdin, stdout, stderr, timeout=nil, max=nil)
142
- input ||= ''
143
- max = nil if max && max <= 0
144
- out, err = '', ''
145
- offset = 0
146
-
147
- timeout = nil if timeout && timeout <= 0.0
148
- @runtime = 0.0
149
- start = Time.now
150
-
151
- writers = [stdin]
152
- readers = [stdout, stderr]
153
- t = timeout
154
- while readers.any? || writers.any?
155
- ready = IO.select(readers, writers, readers + writers, t)
156
- raise TimeoutExceeded if ready.nil?
157
-
158
- # write to stdin stream
159
- ready[1].each do |fd|
160
- begin
161
- boom = nil
162
- size = fd.write_nonblock(input)
163
- input = input[size, input.size]
164
- rescue Errno::EPIPE => boom
165
- rescue Errno::EAGAIN, Errno::EINTR
166
- end
167
- if boom || input.size == 0
168
- stdin.close
169
- writers.delete(stdin)
170
- end
171
- end
172
-
173
- # read from stdout and stderr streams
174
- ready[0].each do |fd|
175
- buf = (fd == stdout) ? out : err
176
- begin
177
- buf << fd.readpartial(BUFSIZE)
178
- rescue Errno::EAGAIN, Errno::EINTR
179
- rescue EOFError
180
- readers.delete(fd)
181
- fd.close
182
- end
183
- end
184
-
185
- # keep tabs on the total amount of time we've spent here
186
- @runtime = Time.now - start
187
- if timeout
188
- t = timeout - @runtime
189
- raise TimeoutExceeded if t < 0.0
190
- end
191
-
192
- # maybe we've hit our max output
193
- if max && ready[0].any? && (out.size + err.size) > max
194
- raise MaximumOutputExceeded
195
- end
196
- end
197
-
198
- [out, err]
199
- end
200
-
201
- # Spawn a child process, perform IO redirection and environment prep, and
202
- # return the running process's pid.
203
- #
204
- # This method implements a limited subset of Ruby 1.9's Process::spawn.
205
- # The idea is that we can just use that when available, since most platforms
206
- # will eventually build in special (and hopefully good) support for it.
207
- #
208
- # env - Hash of { name => val } environment variables set in the child
209
- # process.
210
- # argv - New process's argv as an Array. When this value is a string,
211
- # the command may be run through the system /bin/sh or
212
- # options - Supports a subset of Process::spawn options, including:
213
- # :chdir => str to change the directory to str in the child
214
- # FD => :close to close a file descriptor in the child
215
- # :in => FD to redirect child's stdin to FD
216
- # :out => FD to redirect child's stdout to FD
217
- # :err => FD to redirect child's stderr to FD
218
- #
219
- # Returns the pid of the new process as an integer. The process exit status
220
- # must be obtained using Process::waitpid.
221
- def spawn(env, *argv)
222
- options = (argv.pop if argv[-1].kind_of?(Hash)) || {}
223
- fork do
224
- # { fd => :close } in options means close that fd
225
- options.each { |k,v| k.close if v == :close && !k.closed? }
226
-
227
- # reopen stdin, stdout, and stderr on provided fds
228
- STDIN.reopen(options[:in])
229
- STDOUT.reopen(options[:out])
230
- STDERR.reopen(options[:err])
231
-
232
- # setup child environment
233
- env.each { |k, v| ENV[k] = v }
234
-
235
- # { :chdir => '/' } in options means change into that dir
236
- ::Dir.chdir(options[:chdir]) if options[:chdir]
237
-
238
- # do the deed
239
- ::Kernel::exec(*argv)
240
- exit! 1
241
- end
242
- end
243
-
244
- # Start a process with spawn options and return
245
- # popen4([env], command, arg1, arg2, [opt])
246
- #
247
- # env - The child process's environment as a Hash.
248
- # command - The command and zero or more arguments.
249
- # options - An options hash.
250
- #
251
- # See Ruby 1.9 IO.popen and Process::spawn docs for more info:
252
- # http://www.ruby-doc.org/core-1.9/classes/IO.html#M001640
253
- #
254
- # Returns a [pid, stdin, stderr, stdout] tuple where pid is the child
255
- # process's pid, stdin is a writeable IO object, and stdout + stderr are
256
- # readable IO objects.
257
- def popen4(*argv)
258
- # create some pipes (see pipe(2) manual -- the ruby docs suck)
259
- ird, iwr = IO.pipe
260
- ord, owr = IO.pipe
261
- erd, ewr = IO.pipe
262
-
263
- # spawn the child process with either end of pipes hooked together
264
- opts =
265
- ((argv.pop if argv[-1].is_a?(Hash)) || {}).merge(
266
- # redirect fds # close other sides
267
- :in => ird, iwr => :close,
268
- :out => owr, ord => :close,
269
- :err => ewr, erd => :close
270
- )
271
- pid = spawn(*(argv + [opts]))
272
-
273
- [pid, iwr, ord, erd]
274
- ensure
275
- # we're in the parent, close child-side fds
276
- [ird, owr, ewr].each { |fd| fd.close }
277
- end
278
-
279
- # Wait for the child process to exit
280
- #
281
- # Returns the Process::Status object obtained by reaping the process.
282
- def waitpid(pid)
283
- ::Process::waitpid(pid)
284
- $?
285
- end
286
-
287
- # Use native Process::spawn implementation on Ruby 1.9.
288
- if ::Process.respond_to?(:spawn)
289
- def spawn(*argv)
290
- ::Process.spawn(*argv)
291
- end
292
- end
293
- end
294
- end
@@ -1,102 +0,0 @@
1
- require 'albino'
2
- require 'rubygems'
3
- require 'test/unit'
4
-
5
- class TestProcess < Test::Unit::TestCase
6
- def test_argv_array_execs
7
- p = Albino::Process.new(['printf', '%s %s %s', '1', '2', '3 4'])
8
- assert p.success?
9
- assert_equal "1 2 3 4", p.out
10
- end
11
-
12
- def test_argv_string_uses_sh
13
- p = Albino::Process.new("echo via /bin/sh")
14
- assert p.success?
15
- assert_equal "via /bin/sh\n", p.out
16
- end
17
-
18
- def test_stdout
19
- p = Albino::Process.new(['echo', 'boom'])
20
- assert_equal "boom\n", p.out
21
- assert_equal "", p.err
22
- end
23
-
24
- def test_stderr
25
- p = Albino::Process.new('echo boom 1>&2')
26
- assert_equal "", p.out
27
- assert_equal "boom\n", p.err
28
- end
29
-
30
- def test_status
31
- p = Albino::Process.new('exit 3')
32
- assert !p.status.success?
33
- assert_equal 3, p.status.exitstatus
34
- end
35
-
36
- def test_env
37
- p = Albino::Process.new('echo $FOO', { 'FOO' => 'BOOYAH' })
38
- assert_equal "BOOYAH\n", p.out
39
- end
40
-
41
- def test_chdir
42
- p = Albino::Process.new(["pwd"], {}, :chdir => File.dirname(Dir.pwd))
43
- assert_equal File.dirname(Dir.pwd) + "\n", p.out
44
- end
45
-
46
- def test_input
47
- input = "HEY NOW\n" * 100_000 # 800K
48
- p = Albino::Process.new(['wc', '-l'], {}, :input => input)
49
- assert_equal 100_000, p.out.strip.to_i
50
- end
51
-
52
- def test_max
53
- assert_raise Albino::Process::MaximumOutputExceeded do
54
- Albino::Process.new(['yes'], {}, :max => 100_000)
55
- end
56
- end
57
-
58
- def test_max_with_child_hierarchy
59
- assert_raise Albino::Process::MaximumOutputExceeded do
60
- Albino::Process.new(['/bin/sh', '-c', 'yes'], {}, :max => 100_000)
61
- end
62
- end
63
-
64
- def test_max_with_stubborn_child
65
- assert_raise Albino::Process::MaximumOutputExceeded do
66
- Albino::Process.new("trap '' TERM; yes", {}, :max => 100_000)
67
- end
68
- end
69
-
70
- def test_timeout
71
- assert_raise Albino::Process::TimeoutExceeded do
72
- Albino::Process.new(['sleep 1'], {}, :timeout => 0.05)
73
- end
74
- end
75
-
76
- def test_timeout_with_child_hierarchy
77
- assert_raise Albino::Process::TimeoutExceeded do
78
- Albino::Process.new(['/bin/sh', '-c', 'yes'], {}, :timeout => 0.05)
79
- end
80
- end
81
-
82
- def test_lots_of_input_and_lots_of_output_at_the_same_time
83
- input = "stuff on stdin \n" * 1_000
84
- command = "
85
- while read line
86
- do
87
- echo stuff on stdout;
88
- echo stuff on stderr 1>&2;
89
- done
90
- "
91
- p = Albino::Process.new(['/bin/sh', '-c', command], {}, :input => input)
92
- assert_equal input.size, p.out.size
93
- assert_equal input.size, p.err.size
94
- assert p.success?
95
- end
96
-
97
- def test_input_cannot_be_written_due_to_broken_pipe
98
- input = "1" * 100_000
99
- p = Albino::Process.new(['false'], {}, :input => input)
100
- assert !p.success?
101
- end
102
- end