dia 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/dia.gemspec +5 -3
- data/lib/dia.rb +2 -1
- data/lib/dia/application.rb +15 -9
- data/lib/dia/exception_struct.rb +7 -0
- data/lib/dia/exceptions.rb +5 -0
- data/lib/dia/functions.rb +2 -0
- data/lib/dia/profiles.rb +23 -1
- data/lib/dia/ruby_block.rb +143 -91
- data/lib/dia/shared_features.rb +32 -33
- data/test/suite/lib/dia/Application#initialize.rb +21 -0
- data/test/suite/lib/dia/Application#run.rb +16 -0
- data/test/suite/lib/dia/Application#run_nonblock.rb +16 -0
- data/test/suite/lib/dia/RubyBlock#exception.rb +46 -0
- data/test/suite/lib/dia/RubyBlock#exception_raised?.rb +45 -0
- data/test/suite/lib/dia/RubyBlock#redirect_stderr?.rb +27 -0
- data/test/suite/lib/dia/RubyBlock#redirect_stdout?.rb +27 -0
- data/test/suite/lib/dia/RubyBlock#rescue_exception?.rb +28 -0
- data/test/suite/lib/dia/RubyBlock#run.rb +135 -0
- data/test/suite/lib/dia/RubyBlock#run_nonblock.rb +150 -0
- data/test/suite/lib/dia/RubyBlock#stderr.rb +48 -0
- data/test/suite/lib/dia/RubyBlock#stdout.rb +43 -0
- data/test/suite/lib/dia/SharedFeatures#exit_status.rb +26 -0
- data/test/suite/lib/dia/SharedFeatures#pid.rb +26 -0
- data/test/suite/lib/dia/SharedFeatures#running?.rb +37 -0
- data/test/suite/lib/dia/SharedFeatures#terminate.rb +26 -0
- metadata +41 -11
- data/test/suite/lib/dia/exception_struct.rb +0 -40
- data/test/suite/lib/dia/ruby_block.rb +0 -561
- data/test/suite/lib/dia/shared_features.rb +0 -236
data/lib/dia/shared_features.rb
CHANGED
@@ -1,24 +1,29 @@
|
|
1
1
|
module Dia
|
2
2
|
|
3
|
+
|
4
|
+
# The SharedFeatures module implements methods which are shared by
|
5
|
+
# {Dia::Application Dia::Application} and {Dia::RubyBlock Dia::RubyBlock}.
|
6
|
+
# This module shoudln't be interacted with directly.
|
3
7
|
module SharedFeatures
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
+
|
9
|
+
attr_reader :pid
|
10
|
+
|
11
|
+
# @return [Fixnum] Returns the Process ID(PID) of the last child process to execute a
|
12
|
+
# a sandbox.
|
13
|
+
#
|
14
|
+
# @return [nil] Returns nil if #run or #run_nonblock have not been called.
|
8
15
|
def pid
|
9
16
|
@pid
|
10
17
|
end
|
11
18
|
|
12
|
-
# The exit_status method will return the exit status of
|
13
|
-
#
|
14
|
-
# when being used with #run_nonblock.
|
19
|
+
# The exit_status method will return the exit status of the child process
|
20
|
+
# used to execute a sandbox.
|
21
|
+
# This method blocks until the spawned process exits when being used with #run_nonblock.
|
22
|
+
#
|
23
|
+
# @return [Fixnum] Returns the exit status as a Fixnum.
|
24
|
+
#
|
25
|
+
# @return [nil] Returns nil if #run or #run_nonblock have not been called.
|
15
26
|
#
|
16
|
-
# @return [Fixnum, nil] Returns the exit status of your sandbox as a
|
17
|
-
# Fixnum.
|
18
|
-
# Returns nil if #run or #run_nonblock has not
|
19
|
-
# been called yet.
|
20
|
-
# Returns nil if the process hasn't exited yet and
|
21
|
-
# #run is being used.
|
22
27
|
# @since 1.5
|
23
28
|
def exit_status()
|
24
29
|
unless @exit_status.nil?
|
@@ -27,22 +32,14 @@ module Dia
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
|
-
# The terminate method will
|
31
|
-
#
|
32
|
-
# To prevent the possible accumulation of zombies, this method will
|
33
|
-
# wait to collect the exit status of your sandbox if it doesn't appear
|
34
|
-
# to have left the process table after sending SIGKILL.
|
35
|
+
# The terminate method will terminate the child process executing a sandbox.
|
35
36
|
#
|
36
|
-
#
|
37
|
-
#
|
37
|
+
# @raise [SystemCallError] It could raise a number of subclasses of SystemCallError
|
38
|
+
# if a signal could not be sent to the child process.
|
38
39
|
#
|
39
|
-
# @
|
40
|
-
# SystemCallError if a call to Process.kill
|
41
|
-
# was unsuccessful
|
40
|
+
# @return [Fixnum] Returns 1 when successful.
|
42
41
|
#
|
43
|
-
# @return [
|
44
|
-
# Returns nil if #run or #run_nonblock has not
|
45
|
-
# been called yet.
|
42
|
+
# @return [nil] Returns nil if #run or #run_nonblock has not been called.
|
46
43
|
def terminate()
|
47
44
|
ret = Process.kill('SIGKILL', @pid) unless @pid.nil?
|
48
45
|
# precaution against the collection of zombie processes.
|
@@ -50,15 +47,17 @@ module Dia
|
|
50
47
|
ret
|
51
48
|
end
|
52
49
|
|
53
|
-
# This method
|
50
|
+
# This method can tell you whether the child process used to execute your sandbox is
|
51
|
+
# running or not.
|
52
|
+
#
|
53
|
+
# @raise [SystemCallError] It could raise a number of subclasses of SystemCallError
|
54
|
+
# if a signal could not be sent to the child process.
|
55
|
+
#
|
56
|
+
# @return [true] Returns true when the child process is running.
|
54
57
|
#
|
55
|
-
# @
|
56
|
-
# if a signal cannot be sent to the process running
|
57
|
-
# a sandbox.
|
58
|
+
# @return [false] Returns false when the child process is not running.
|
58
59
|
#
|
59
|
-
# @return [
|
60
|
-
# Returns nil if #run or #run_nonblock has
|
61
|
-
# not been called yet.
|
60
|
+
# @return [nil] Returns nil if #run or #run_nonblock have not been called.
|
62
61
|
def running?()
|
63
62
|
if @pid.nil?
|
64
63
|
nil
|
@@ -0,0 +1,21 @@
|
|
1
|
+
suite('Application') do
|
2
|
+
suite('#initialize') do
|
3
|
+
suite('Raises') do
|
4
|
+
|
5
|
+
exercise("When the Dia::Profiles::NO_OS_SERVICES profile is supplied to #initialize.") do
|
6
|
+
begin
|
7
|
+
dia = Dia::Application.new(Dia::Profiles::NO_OS_SERVICES, "touch /tmp/bar")
|
8
|
+
rescue ArgumentError => e
|
9
|
+
@result = true
|
10
|
+
else
|
11
|
+
@result = false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
verify('#initialize will raise an ArgumentError.') do
|
16
|
+
@result
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
suite('Application') do
|
2
|
+
suite('#run') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When #run has finished executing.') do
|
6
|
+
dia = Dia::Application.new(Dia::Profiles::NO_INTERNET, "touch /tmp/bar")
|
7
|
+
@result = dia.run
|
8
|
+
end
|
9
|
+
|
10
|
+
verify('It returns the Process ID of the spawned process.') do
|
11
|
+
@result.class == Fixnum
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
suite('Application') do
|
2
|
+
suite('#run_nonblock') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When #run_nonblock has finished executing.') do
|
6
|
+
dia = Dia::Application.new(Dia::Profiles::NO_INTERNET, "touch /tmp/bar")
|
7
|
+
@result = dia.run
|
8
|
+
end
|
9
|
+
|
10
|
+
verify('It returns the Process ID of the spawned process.') do
|
11
|
+
@result.class == Fixnum
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#exception') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When exceptions are being captured, and an exception is raised.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
7
|
+
raise(StandardError, "raised")
|
8
|
+
end
|
9
|
+
|
10
|
+
dia.rescue_exception = true
|
11
|
+
dia.run
|
12
|
+
@result = dia.exception
|
13
|
+
end
|
14
|
+
|
15
|
+
verify('#exception returns a Dia::ExceptionStruct object.') do
|
16
|
+
@result.class == Dia::ExceptionStruct
|
17
|
+
end
|
18
|
+
|
19
|
+
exercise('When exceptions are being captured, and no exception is raised.') do
|
20
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
21
|
+
dia.rescue_exception = true
|
22
|
+
dia.run
|
23
|
+
@result = dia.exception
|
24
|
+
end
|
25
|
+
|
26
|
+
verify('#exception returns nil as no exception is available.') do
|
27
|
+
@result == nil
|
28
|
+
end
|
29
|
+
|
30
|
+
exercise('When exceptions are not being captured, and an exception is raised.') do
|
31
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
32
|
+
raise(StandardError, "raised")
|
33
|
+
end
|
34
|
+
dia.redirect_stderr = true # I don't want the test output to be flooded with a backtrace.
|
35
|
+
dia.rescue_exception = false # default, but lets be explicit.
|
36
|
+
dia.run
|
37
|
+
@result = dia.exception
|
38
|
+
end
|
39
|
+
|
40
|
+
verify('#exception returns nil.') do
|
41
|
+
@result == nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#exception_raised?') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When an exception has been raised.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
7
|
+
raise(StandardError, "raised")
|
8
|
+
end
|
9
|
+
dia.rescue_exception = true
|
10
|
+
dia.run
|
11
|
+
@result = dia.exception_raised?
|
12
|
+
end
|
13
|
+
|
14
|
+
verify('#exception_raised? returns true.') do
|
15
|
+
@result == true
|
16
|
+
end
|
17
|
+
|
18
|
+
exercise('When an exception has not been raised.') do
|
19
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
20
|
+
dia.rescue_exception = true
|
21
|
+
dia.run
|
22
|
+
@result = dia.exception_raised?
|
23
|
+
end
|
24
|
+
|
25
|
+
verify('#exception_raised? returns false.') do
|
26
|
+
@result == false
|
27
|
+
end
|
28
|
+
|
29
|
+
exercise('When exceptions are not being captured, and an exception has been raised.') do
|
30
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
31
|
+
raise(StandardError, "raised")
|
32
|
+
end
|
33
|
+
dia.rescue_exception = false # default, but lets be explicit.
|
34
|
+
dia.redirect_stderr = true # I don't want the tests to be flooded with a backtrace.
|
35
|
+
dia.run
|
36
|
+
@result = dia.exception_raised?
|
37
|
+
end
|
38
|
+
|
39
|
+
verify('#exception_raised? returns false') do
|
40
|
+
@result == false
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#redirect_stderr?') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When stderr output is being redirected.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
7
|
+
dia.redirect_stderr = true
|
8
|
+
@result = dia.redirect_stderr?
|
9
|
+
end
|
10
|
+
|
11
|
+
verify('#redirect_stderr? returns true.') do
|
12
|
+
@result == true
|
13
|
+
end
|
14
|
+
|
15
|
+
exercise('When stderr output is not being redirected.') do
|
16
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
17
|
+
dia.redirect_stderr = false
|
18
|
+
@result = dia.redirect_stderr?
|
19
|
+
end
|
20
|
+
|
21
|
+
verify('#redirect_stderr? returns false.') do
|
22
|
+
@result == false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#redirect_stdout?') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When stdout is being redirected.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
7
|
+
dia.redirect_stdout = true
|
8
|
+
@result = dia.redirect_stdout?
|
9
|
+
end
|
10
|
+
|
11
|
+
verify('#redirect_stdout? returns true.') do
|
12
|
+
@result == true
|
13
|
+
end
|
14
|
+
|
15
|
+
exercise('When stdout is not being redirected.') do
|
16
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
17
|
+
dia.redirect_stdout = false
|
18
|
+
@result = dia.redirect_stdout?
|
19
|
+
end
|
20
|
+
|
21
|
+
verify('#redirect_stdout? returns false.') do
|
22
|
+
@result == false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#rescue_exception?') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When exceptions are being captured.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
7
|
+
dia.rescue_exception = true
|
8
|
+
@result = dia.rescue_exception?
|
9
|
+
end
|
10
|
+
|
11
|
+
verify('#rescue_exception? returns true') do
|
12
|
+
@result == true
|
13
|
+
end
|
14
|
+
|
15
|
+
exercise('When exceptions are not being captured.') do
|
16
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
17
|
+
dia.rescue_exception = false # default, but lets be explicit.
|
18
|
+
@result = dia.rescue_exception?
|
19
|
+
end
|
20
|
+
|
21
|
+
verify('#rescue_exception? returns false') do
|
22
|
+
@result == false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,135 @@
|
|
1
|
+
suite('RubyBlock') do
|
2
|
+
suite('#run') do
|
3
|
+
suite('Return values') do
|
4
|
+
|
5
|
+
exercise('When #run has finished executing.') do
|
6
|
+
dia = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) { }
|
7
|
+
@result = dia.run
|
8
|
+
end
|
9
|
+
|
10
|
+
verify('It returns the Process ID of the spawned process.') do
|
11
|
+
@result.class == Fixnum
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
suite('Behavior') do
|
17
|
+
|
18
|
+
setup do
|
19
|
+
@result = nil
|
20
|
+
@reader, @writer = IO.pipe
|
21
|
+
end
|
22
|
+
|
23
|
+
exercise('Confirm the profile ' \
|
24
|
+
'Dia::Profiles::NO_INTERNET ' \
|
25
|
+
'is creating a working sandbox environment.') do
|
26
|
+
sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_INTERNET) do
|
27
|
+
begin
|
28
|
+
@reader.close
|
29
|
+
TCPSocket.open('http://www.google.com', 80)
|
30
|
+
@writer.write('false')
|
31
|
+
rescue SocketError, SystemCallError => e
|
32
|
+
@writer.write('true')
|
33
|
+
ensure
|
34
|
+
@writer.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
sandbox.run
|
39
|
+
|
40
|
+
@writer.close
|
41
|
+
@result = @reader.gets
|
42
|
+
@reader.close
|
43
|
+
end
|
44
|
+
|
45
|
+
verify(nil) do
|
46
|
+
@result == 'true'
|
47
|
+
end
|
48
|
+
|
49
|
+
exercise('Confirm the profile ' \
|
50
|
+
'Dia::Profiles::NO_FILESYSTEM_WRITE ' \
|
51
|
+
'is creating a working sandbox environment.') do
|
52
|
+
|
53
|
+
sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE) do
|
54
|
+
begin
|
55
|
+
@reader.close
|
56
|
+
File.open('/tmp/foo.txt', 'w') { |f| f.puts('fail') }
|
57
|
+
@writer.write('false')
|
58
|
+
rescue SocketError, SystemCallError => e
|
59
|
+
@writer.write('true')
|
60
|
+
ensure
|
61
|
+
@writer.close
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
sandbox.run
|
67
|
+
|
68
|
+
@writer.close
|
69
|
+
@result = @reader.gets
|
70
|
+
@reader.close
|
71
|
+
end
|
72
|
+
|
73
|
+
verify(nil) do
|
74
|
+
@result == 'true'
|
75
|
+
end
|
76
|
+
|
77
|
+
exercise('Confirm the profile ' \
|
78
|
+
'Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP ' \
|
79
|
+
'is creating a working sandbox environment. ') do
|
80
|
+
sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_FILESYSTEM_WRITE_EXCEPT_TMP) do
|
81
|
+
begin
|
82
|
+
@reader.close
|
83
|
+
out = Time.now.to_s
|
84
|
+
File.open('/tmp/%s.dia_test' % [ out ] , 'w') { |f| f.puts('success') }
|
85
|
+
File.open(File.join(ENV['HOME'], 'fail.txt')) { |f| f.puts('fail') }
|
86
|
+
@writer.write('false')
|
87
|
+
rescue SocketError, SystemCallError => e
|
88
|
+
if File.exists?('/tmp/%s.dia_test' % [ out ])
|
89
|
+
@writer.write('true')
|
90
|
+
else
|
91
|
+
@writer.write('false')
|
92
|
+
end
|
93
|
+
ensure
|
94
|
+
@writer.close
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
sandbox.run
|
99
|
+
|
100
|
+
@writer.close
|
101
|
+
@result = @reader.gets
|
102
|
+
@reader.close
|
103
|
+
end
|
104
|
+
|
105
|
+
verify(nil) do
|
106
|
+
@result == 'true'
|
107
|
+
end
|
108
|
+
|
109
|
+
exercise('Confirm the profile ' \
|
110
|
+
'Dia::Profiles::NO_NETWORKING ' \
|
111
|
+
'is creating a working sandbox environment') do
|
112
|
+
sandbox = Dia::RubyBlock.new(Dia::Profiles::NO_NETWORKING) do
|
113
|
+
begin
|
114
|
+
@reader.close
|
115
|
+
TCPSocket.open('http://www.youtube.com', 80)
|
116
|
+
@writer.write('false')
|
117
|
+
rescue SocketError => e
|
118
|
+
@writer.write('true')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
sandbox.run
|
123
|
+
|
124
|
+
@writer.close
|
125
|
+
@result = @reader.gets
|
126
|
+
@reader.close
|
127
|
+
end
|
128
|
+
|
129
|
+
verify(nil) do
|
130
|
+
@result == 'true'
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|