dia 2.0.0 → 2.0.1
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.
- 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
|